Mostly security fixes.

nci: add data_len bound checks to activation parameter extractors
 nci: fix double completion race in nci_data_exchange_complete
 nci: fix out-of-bounds write in nci_target_auto_activated()
 nci: fix uninit-value in the RF discover/activated NTF handlers
 nci: fix use of uninitialized memory in CORE_INIT_RSP parsing
 nci: free destination parameters when closing a connection
 llcp: avoid userspace overflow on invalid optlen
 llcp: bound SNL TLV parsing to the skb and add length checks
 llcp: bound the connect_sn TLV walk to the skb
 llcp: fix OOB read and u8 offset wrap in TLV parsers
 llcp: read llcp_sock->local under the socket lock in getsockopt
 llcp: reject PDUs shorter than the LLCP header
 digital: Do not dump a NULL response in command completion
 digital: clamp SENSF_RES length to the destination buffer
 fdp: bound the device-reported read length and fix an skb leak
 microread: validate target discovery payload lengths
 pn533: hold a reference to the request skb during send_frame
 pn533: purge fragmented skbs during cleanup
 st21nfca: validate ATR_REQ length against the received frame
 
 Signed-off-by: David Heidelberg <david@ixit.cz>
 -----BEGIN PGP SIGNATURE-----
 
 iQIzBAABCAAdFiEE13oJz+7cK71TpwR0YAI/xNNJIHIFAmp7TEMACgkQYAI/xNNJ
 IHLe8A/+Nf5frmGy4C/J63sSWxFCvk1DXNp9zDkgQ5p5Tb2qcIxJzJsPeSyvQ7qG
 B3NUQ6vPxJu5alkiYGkqnpFIEedIHvHE3maFQB4vqMiuxUqNFhNEk2CjlQde5K3Y
 7exkGce57K/9ZkSp3wiz1OasCe0jI/Jo6wI1/t0oYXGo307OdGga39nXNtA1svha
 3a82+ViWoZaole+zGLZLl8mrtJQwooMiW7UXlEvvyggQndP728jtK3gcGwMmylyE
 tP8aDkJjcfe9L5TXHtom5NWEqN5PzzUu8qCGtnj9hC8OxOB3hFSyPeOvFwbiNbb7
 ZkfAKNz02ifjUFw9TTwwjZDiISVpDmti8bOO88hHmaS8FzIkDrEX6/XGcdJzRQvM
 r/5c7wBK6tlwaIRzVo2UtJ6E7fsRptTwmFXbCq4m1qzGrPJ4BefK8QAr0Dx6jk5h
 IeXaERxuZus2t/8OdwN2vQ8+fFMtfMXytGWScpCQmIe19zBzvM+fPg5CNwqd5sor
 ndNb1y8NjiifVAOdFuTS8023IPjQS2GRrMNaHiMP4t9Sht2ihScolzlCzhOYDw6Z
 cLn6bmIUTuXnkyP+xBrb2pi25myPAJ94gNDjyY7bQVrkeT6p8S9cob4deh4wyQfK
 GyAfIbVGQguL6KxR26Y2iX70iEJSCeB83+JtWNgWzDneSUiBwsY=
 =A3SJ
 -----END PGP SIGNATURE-----

Merge tag 'nfc-7.1-rc8' of https://codeberg.org/linux-nfc/linux

David Heidelberg says:

====================
Mostly security fixes.

nci: add data_len bound checks to activation parameter extractors
nci: fix double completion race in nci_data_exchange_complete
nci: fix out-of-bounds write in nci_target_auto_activated()
nci: fix uninit-value in the RF discover/activated NTF handlers
nci: fix use of uninitialized memory in CORE_INIT_RSP parsing
nci: free destination parameters when closing a connection
llcp: avoid userspace overflow on invalid optlen
llcp: bound SNL TLV parsing to the skb and add length checks
llcp: bound the connect_sn TLV walk to the skb
llcp: fix OOB read and u8 offset wrap in TLV parsers
llcp: read llcp_sock->local under the socket lock in getsockopt
llcp: reject PDUs shorter than the LLCP header
digital: Do not dump a NULL response in command completion
digital: clamp SENSF_RES length to the destination buffer
fdp: bound the device-reported read length and fix an skb leak
microread: validate target discovery payload lengths
pn533: hold a reference to the request skb during send_frame
pn533: purge fragmented skbs during cleanup
st21nfca: validate ATR_REQ length against the received frame

* tag 'nfc-7.1-rc8' of https://codeberg.org/linux-nfc/linux:
  nfc: microread: validate target discovery payload lengths
  nfc: nci: free destination parameters when closing a connection
  nfc: nci: fix use of uninitialized memory in CORE_INIT_RSP parsing
  nfc: pn533: purge fragmented skbs during cleanup
  nfc: st21nfca: validate ATR_REQ length against the received frame
  nfc: llcp: bound the connect_sn TLV walk to the skb
  nfc: llcp: reject PDUs shorter than the LLCP header
  nfc: digital: Do not dump a NULL response in command completion
  nfc: pn533: hold a reference to the request skb during send_frame
  nfc: nci: fix uninit-value in the RF discover/activated NTF handlers
  nfc: fdp: bound the device-reported read length and fix an skb leak
  nfc: nci: fix out-of-bounds write in nci_target_auto_activated()
  nfc: nci: add data_len bound checks to activation parameter extractors
  nfc: llcp: fix OOB read and u8 offset wrap in TLV parsers
  nfc: llcp: bound SNL TLV parsing to the skb and add length checks
  nfc: digital: clamp SENSF_RES length to the destination buffer
  nfc: nci: fix double completion race in nci_data_exchange_complete
  nfc: llcp: read llcp_sock->local under the socket lock in getsockopt
  nfc: llcp: avoid userspace overflow on invalid optlen
====================

Link: https://patch.msgid.link/
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
This commit is contained in:
Jakub Kicinski 2026-08-17 11:45:00 -07:00
commit a22054d743
12 changed files with 211 additions and 40 deletions

View File

@ -166,9 +166,36 @@ static int fdp_nci_i2c_read(struct fdp_i2c_phy *phy, struct sk_buff **skb)
/* Packet that contains a length */
if (tmp[0] == 0 && tmp[1] == 0) {
phy->next_read_size = (tmp[2] << 8) + tmp[3] + 3;
/*
* next_read_size is taken from the device and is used
* as the i2c_master_recv() count for the next packet
* and as the data skb size. A value above the receive
* buffer overflows tmp[]; one below the minimum frame
* size runs the header/LRC strip and the length-field
* read past a short receive. Either way the packet is
* corrupt: drop it and force resynchronization.
*/
if (phy->next_read_size < FDP_NCI_I2C_MIN_PAYLOAD ||
phy->next_read_size > FDP_NCI_I2C_MAX_PAYLOAD) {
dev_dbg(&client->dev, "%s: corrupted packet\n",
__func__);
phy->next_read_size = FDP_NCI_I2C_MIN_PAYLOAD;
goto flush;
}
} else {
phy->next_read_size = FDP_NCI_I2C_MIN_PAYLOAD;
/*
* Only one data packet is delivered per call; if the
* device sends another, do not overwrite and leak the
* skb allocated for the previous one.
*/
if (*skb) {
kfree_skb(*skb);
*skb = NULL;
}
*skb = alloc_skb(len, GFP_KERNEL);
if (*skb == NULL) {
r = -ENOMEM;

View File

@ -483,13 +483,19 @@ static void microread_target_discovered(struct nfc_hci_dev *hdev, u8 gate,
switch (gate) {
case MICROREAD_GATE_ID_MREAD_ISO_A:
if (skb->len <= MICROREAD_EMCF_A_LEN) {
r = -EINVAL;
goto exit_free;
}
targets->supported_protocols =
nfc_hci_sak_to_protocol(skb->data[MICROREAD_EMCF_A_SAK]);
targets->sens_res =
be16_to_cpu(*(u16 *)&skb->data[MICROREAD_EMCF_A_ATQA]);
targets->sel_res = skb->data[MICROREAD_EMCF_A_SAK];
targets->nfcid1_len = skb->data[MICROREAD_EMCF_A_LEN];
if (targets->nfcid1_len > sizeof(targets->nfcid1)) {
if (targets->nfcid1_len > sizeof(targets->nfcid1) ||
targets->nfcid1_len > skb->len - MICROREAD_EMCF_A_UID) {
r = -EINVAL;
goto exit_free;
}
@ -497,13 +503,19 @@ static void microread_target_discovered(struct nfc_hci_dev *hdev, u8 gate,
targets->nfcid1_len);
break;
case MICROREAD_GATE_ID_MREAD_ISO_A_3:
if (skb->len <= MICROREAD_EMCF_A3_LEN) {
r = -EINVAL;
goto exit_free;
}
targets->supported_protocols =
nfc_hci_sak_to_protocol(skb->data[MICROREAD_EMCF_A3_SAK]);
targets->sens_res =
be16_to_cpu(*(u16 *)&skb->data[MICROREAD_EMCF_A3_ATQA]);
targets->sel_res = skb->data[MICROREAD_EMCF_A3_SAK];
targets->nfcid1_len = skb->data[MICROREAD_EMCF_A3_LEN];
if (targets->nfcid1_len > sizeof(targets->nfcid1)) {
if (targets->nfcid1_len > sizeof(targets->nfcid1) ||
targets->nfcid1_len > skb->len - MICROREAD_EMCF_A3_UID) {
r = -EINVAL;
goto exit_free;
}
@ -511,11 +523,21 @@ static void microread_target_discovered(struct nfc_hci_dev *hdev, u8 gate,
targets->nfcid1_len);
break;
case MICROREAD_GATE_ID_MREAD_ISO_B:
if (skb->len < MICROREAD_EMCF_B_UID + 4) {
r = -EINVAL;
goto exit_free;
}
targets->supported_protocols = NFC_PROTO_ISO14443_B_MASK;
memcpy(targets->nfcid1, &skb->data[MICROREAD_EMCF_B_UID], 4);
targets->nfcid1_len = 4;
break;
case MICROREAD_GATE_ID_MREAD_NFC_T1:
if (skb->len < MICROREAD_EMCF_T1_UID + 4) {
r = -EINVAL;
goto exit_free;
}
targets->supported_protocols = NFC_PROTO_JEWEL_MASK;
targets->sens_res =
le16_to_cpu(*(u16 *)&skb->data[MICROREAD_EMCF_T1_ATQA]);
@ -523,6 +545,11 @@ static void microread_target_discovered(struct nfc_hci_dev *hdev, u8 gate,
targets->nfcid1_len = 4;
break;
case MICROREAD_GATE_ID_MREAD_NFC_T3:
if (skb->len < MICROREAD_EMCF_T3_UID + 8) {
r = -EINVAL;
goto exit_free;
}
targets->supported_protocols = NFC_PROTO_FELICA_MASK;
memcpy(targets->nfcid1, &skb->data[MICROREAD_EMCF_T3_UID], 8);
targets->nfcid1_len = 8;

View File

@ -434,6 +434,18 @@ static int pn533_send_async_complete(struct pn533 *dev)
return rc;
}
static int pn533_send_cmd_frame(struct pn533 *dev, struct pn533_cmd *cmd)
{
struct sk_buff *req = cmd->req;
int rc;
skb_get(req);
dev->cmd = cmd;
rc = dev->phy_ops->send_frame(dev, req);
dev_kfree_skb(req);
return rc;
}
static int __pn533_send_async(struct pn533 *dev, u8 cmd_code,
struct sk_buff *req,
pn533_send_async_complete_t complete_cb,
@ -458,8 +470,7 @@ static int __pn533_send_async(struct pn533 *dev, u8 cmd_code,
mutex_lock(&dev->cmd_lock);
if (!dev->cmd_pending) {
dev->cmd = cmd;
rc = dev->phy_ops->send_frame(dev, req);
rc = pn533_send_cmd_frame(dev, cmd);
if (rc) {
dev->cmd = NULL;
goto error;
@ -529,8 +540,7 @@ static int pn533_send_cmd_direct_async(struct pn533 *dev, u8 cmd_code,
pn533_build_cmd_frame(dev, cmd_code, req);
dev->cmd = cmd;
rc = dev->phy_ops->send_frame(dev, req);
rc = pn533_send_cmd_frame(dev, cmd);
if (rc < 0) {
dev->cmd = NULL;
kfree(cmd);
@ -569,8 +579,7 @@ static void pn533_wq_cmd(struct work_struct *work)
mutex_unlock(&dev->cmd_lock);
dev->cmd = cmd;
rc = dev->phy_ops->send_frame(dev, cmd->req);
rc = pn533_send_cmd_frame(dev, cmd);
if (rc < 0) {
dev->cmd = NULL;
dev_kfree_skb(cmd->req);
@ -2799,6 +2808,7 @@ void pn53x_common_clean(struct pn533 *priv)
destroy_workqueue(priv->wq);
skb_queue_purge(&priv->resp_q);
skb_queue_purge(&priv->fragment_skb);
list_for_each_entry_safe(cmd, n, &priv->cmd_queue, queue) {
list_del(&cmd->queue);

View File

@ -205,6 +205,9 @@ static int st21nfca_tm_recv_atr_req(struct nfc_hci_dev *hdev,
if (atr_req->length < sizeof(struct st21nfca_atr_req))
return -EPROTO;
if (atr_req->length > skb->len)
return -EPROTO;
r = st21nfca_tm_send_atr_res(hdev, atr_req);
if (r)
return r;

View File

@ -127,7 +127,7 @@ static void digital_wq_cmd_complete(struct work_struct *work)
mutex_unlock(&ddev->cmd_lock);
if (!IS_ERR(cmd->resp))
if (!IS_ERR_OR_NULL(cmd->resp))
print_hex_dump_debug("DIGITAL RX: ", DUMP_PREFIX_NONE, 16, 1,
cmd->resp->data, cmd->resp->len, false);

View File

@ -778,6 +778,8 @@ static void digital_in_recv_sensf_res(struct nfc_digital_dev *ddev, void *arg,
sensf_res = (struct digital_sensf_res *)resp->data;
resp->len = min_t(unsigned int, resp->len, NFC_SENSF_RES_MAXSIZE);
memcpy(target.sensf_res, sensf_res, resp->len);
target.sensf_res_len = resp->len;

View File

@ -193,7 +193,8 @@ int nfc_llcp_parse_gb_tlv(struct nfc_llcp_local *local,
const u8 *tlv_array, u16 tlv_array_len)
{
const u8 *tlv = tlv_array;
u8 type, length, offset = 0;
u8 type, length;
u16 offset = 0;
pr_debug("TLV array length %d\n", tlv_array_len);
@ -201,9 +202,15 @@ int nfc_llcp_parse_gb_tlv(struct nfc_llcp_local *local,
return -ENODEV;
while (offset < tlv_array_len) {
if (offset + 2 > tlv_array_len)
return -EINVAL;
type = tlv[0];
length = tlv[1];
if (offset + 2 + length > tlv_array_len)
return -EINVAL;
pr_debug("type 0x%x length %d\n", type, length);
switch (type) {
@ -243,7 +250,8 @@ int nfc_llcp_parse_connection_tlv(struct nfc_llcp_sock *sock,
const u8 *tlv_array, u16 tlv_array_len)
{
const u8 *tlv = tlv_array;
u8 type, length, offset = 0;
u8 type, length;
u16 offset = 0;
pr_debug("TLV array length %d\n", tlv_array_len);
@ -251,9 +259,15 @@ int nfc_llcp_parse_connection_tlv(struct nfc_llcp_sock *sock,
return -ENOTCONN;
while (offset < tlv_array_len) {
if (offset + 2 > tlv_array_len)
return -EINVAL;
type = tlv[0];
length = tlv[1];
if (offset + 2 + length > tlv_array_len)
return -EINVAL;
pr_debug("type 0x%x length %d\n", type, length);
switch (type) {

View File

@ -849,13 +849,16 @@ static struct nfc_llcp_sock *nfc_llcp_sock_get_sn(struct nfc_llcp_local *local,
static const u8 *nfc_llcp_connect_sn(const struct sk_buff *skb, size_t *sn_len)
{
u8 type, length;
const u8 *tlv = &skb->data[2];
size_t tlv_array_len = skb->len - LLCP_HEADER_SIZE, offset = 0;
const u8 *tlv = &skb->data[LLCP_HEADER_SIZE];
const u8 *tlv_end = skb_tail_pointer(skb);
while (offset < tlv_array_len) {
while (tlv + 2 < tlv_end) {
type = tlv[0];
length = tlv[1];
if (tlv + 2 + length > tlv_end)
break;
pr_debug("type 0x%x length %d\n", type, length);
if (type == LLCP_TLV_SN) {
@ -863,7 +866,6 @@ static const u8 *nfc_llcp_connect_sn(const struct sk_buff *skb, size_t *sn_len)
return &tlv[2];
}
offset += length + 2;
tlv += length + 2;
}
@ -1286,10 +1288,9 @@ static void nfc_llcp_recv_snl(struct nfc_llcp_local *local,
{
struct nfc_llcp_sock *llcp_sock;
u8 dsap, ssap, type, length, tid, sap;
const u8 *tlv;
u16 tlv_len, offset;
const u8 *tlv, *tlv_end;
const char *service_name;
size_t service_name_len;
int service_name_len;
struct nfc_llcp_sdp_tlv *sdp;
HLIST_HEAD(llc_sdres_list);
size_t sdres_tlvs_len;
@ -1305,22 +1306,34 @@ static void nfc_llcp_recv_snl(struct nfc_llcp_local *local,
return;
}
/*
* Walk the SNL TLV list in the linear part of the skb only,
* bounded by skb_tail_pointer(). Each TLV needs a two-byte
* header (type, length) and its declared length must fit before
* the end; this also keeps the walk safe for very short frames.
*/
tlv = &skb->data[LLCP_HEADER_SIZE];
tlv_len = skb->len - LLCP_HEADER_SIZE;
offset = 0;
tlv_end = skb_tail_pointer(skb);
sdres_tlvs_len = 0;
while (offset < tlv_len) {
while (tlv + 2 < tlv_end) {
type = tlv[0];
length = tlv[1];
if (tlv + 2 + length > tlv_end)
break;
switch (type) {
case LLCP_TLV_SDREQ:
if (length < 1)
break;
tid = tlv[2];
service_name = (char *) &tlv[3];
service_name_len = length - 1;
pr_debug("Looking for %.16s\n", service_name);
pr_debug("Looking for %.*s\n", service_name_len,
service_name);
if (service_name_len == strlen("urn:nfc:sn:sdp") &&
!strncmp(service_name, "urn:nfc:sn:sdp",
@ -1380,6 +1393,9 @@ static void nfc_llcp_recv_snl(struct nfc_llcp_local *local,
break;
case LLCP_TLV_SDRES:
if (length != 2)
break;
mutex_lock(&local->sdreq_lock);
pr_debug("LLCP_TLV_SDRES: searching tid %d\n", tlv[2]);
@ -1408,7 +1424,6 @@ static void nfc_llcp_recv_snl(struct nfc_llcp_local *local,
break;
}
offset += length + 2;
tlv += length + 2;
}
@ -1552,6 +1567,11 @@ static void nfc_llcp_rx_work(struct work_struct *work)
static void __nfc_llcp_recv(struct nfc_llcp_local *local, struct sk_buff *skb)
{
if (!pskb_may_pull(skb, LLCP_HEADER_SIZE)) {
kfree_skb(skb);
return;
}
local->rx_pending = skb;
timer_delete(&local->link_timer);
schedule_work(&local->rx_work);

View File

@ -319,14 +319,22 @@ static int nfc_llcp_getsockopt(struct socket *sock, int level, int optname,
if (get_user(len, optlen))
return -EFAULT;
local = llcp_sock->local;
if (!local)
return -ENODEV;
if (len < 0)
return -EINVAL;
if (len < sizeof(u32))
return -EINVAL;
len = min_t(u32, len, sizeof(u32));
lock_sock(sk);
local = llcp_sock->local;
if (!local) {
release_sock(sk);
return -ENODEV;
}
switch (optname) {
case NFC_LLCP_RW:
rw = llcp_sock->rw > LLCP_MAX_RW ? local->rw : llcp_sock->rw;

View File

@ -46,11 +46,11 @@ void nci_data_exchange_complete(struct nci_dev *ndev, struct sk_buff *skb,
timer_delete_sync(&ndev->data_timer);
clear_bit(NCI_DATA_EXCHANGE_TO, &ndev->flags);
/* Mark the exchange as done before calling the callback.
* The callback (e.g. rawsock_data_exchange_complete) may
* want to immediately queue another data exchange.
*/
clear_bit(NCI_DATA_EXCHANGE, &ndev->flags);
/* Claim completion atomically -- both close and rx_work may race here */
if (!test_and_clear_bit(NCI_DATA_EXCHANGE, &ndev->flags)) {
kfree_skb(skb);
return;
}
if (cb) {
/* forward skb to nfc core */

View File

@ -440,7 +440,7 @@ void nci_clear_target_list(struct nci_dev *ndev)
static int nci_rf_discover_ntf_packet(struct nci_dev *ndev,
const struct sk_buff *skb)
{
struct nci_rf_discover_ntf ntf;
struct nci_rf_discover_ntf ntf = {};
const __u8 *data;
bool add_target = true;
@ -525,15 +525,19 @@ static int nci_rf_discover_ntf_packet(struct nci_dev *ndev,
static int nci_extract_activation_params_iso_dep(struct nci_dev *ndev,
struct nci_rf_intf_activated_ntf *ntf,
const __u8 *data)
const __u8 *data, __u8 data_len)
{
struct activation_params_nfca_poll_iso_dep *nfca_poll;
struct activation_params_nfcb_poll_iso_dep *nfcb_poll;
switch (ntf->activation_rf_tech_and_mode) {
case NCI_NFC_A_PASSIVE_POLL_MODE:
if (data_len < 1)
return NCI_STATUS_RF_PROTOCOL_ERROR;
nfca_poll = &ntf->activation_params.nfca_poll_iso_dep;
nfca_poll->rats_res_len = min_t(__u8, *data++, NFC_ATS_MAXSIZE);
data_len--;
nfca_poll->rats_res_len = min_t(__u8, nfca_poll->rats_res_len, data_len);
pr_debug("rats_res_len %d\n", nfca_poll->rats_res_len);
if (nfca_poll->rats_res_len > 0) {
memcpy(nfca_poll->rats_res,
@ -542,8 +546,12 @@ static int nci_extract_activation_params_iso_dep(struct nci_dev *ndev,
break;
case NCI_NFC_B_PASSIVE_POLL_MODE:
if (data_len < 1)
return NCI_STATUS_RF_PROTOCOL_ERROR;
nfcb_poll = &ntf->activation_params.nfcb_poll_iso_dep;
nfcb_poll->attrib_res_len = min_t(__u8, *data++, 50);
data_len--;
nfcb_poll->attrib_res_len = min_t(__u8, nfcb_poll->attrib_res_len, data_len);
pr_debug("attrib_res_len %d\n", nfcb_poll->attrib_res_len);
if (nfcb_poll->attrib_res_len > 0) {
memcpy(nfcb_poll->attrib_res,
@ -562,7 +570,7 @@ static int nci_extract_activation_params_iso_dep(struct nci_dev *ndev,
static int nci_extract_activation_params_nfc_dep(struct nci_dev *ndev,
struct nci_rf_intf_activated_ntf *ntf,
const __u8 *data)
const __u8 *data, __u8 data_len)
{
struct activation_params_poll_nfc_dep *poll;
struct activation_params_listen_nfc_dep *listen;
@ -570,9 +578,13 @@ static int nci_extract_activation_params_nfc_dep(struct nci_dev *ndev,
switch (ntf->activation_rf_tech_and_mode) {
case NCI_NFC_A_PASSIVE_POLL_MODE:
case NCI_NFC_F_PASSIVE_POLL_MODE:
if (data_len < 1)
return NCI_STATUS_RF_PROTOCOL_ERROR;
poll = &ntf->activation_params.poll_nfc_dep;
poll->atr_res_len = min_t(__u8, *data++,
NFC_ATR_RES_MAXSIZE - 2);
data_len--;
poll->atr_res_len = min_t(__u8, poll->atr_res_len, data_len);
pr_debug("atr_res_len %d\n", poll->atr_res_len);
if (poll->atr_res_len > 0)
memcpy(poll->atr_res, data, poll->atr_res_len);
@ -580,9 +592,13 @@ static int nci_extract_activation_params_nfc_dep(struct nci_dev *ndev,
case NCI_NFC_A_PASSIVE_LISTEN_MODE:
case NCI_NFC_F_PASSIVE_LISTEN_MODE:
if (data_len < 1)
return NCI_STATUS_RF_PROTOCOL_ERROR;
listen = &ntf->activation_params.listen_nfc_dep;
listen->atr_req_len = min_t(__u8, *data++,
NFC_ATR_REQ_MAXSIZE - 2);
data_len--;
listen->atr_req_len = min_t(__u8, listen->atr_req_len, data_len);
pr_debug("atr_req_len %d\n", listen->atr_req_len);
if (listen->atr_req_len > 0)
memcpy(listen->atr_req, data, listen->atr_req_len);
@ -603,6 +619,12 @@ static void nci_target_auto_activated(struct nci_dev *ndev,
struct nfc_target *target;
int rc;
/* This is a new target, check if we've enough room */
if (ndev->n_targets == NCI_MAX_DISCOVERED_TARGETS) {
pr_debug("not enough room, ignoring new target...\n");
return;
}
target = &ndev->targets[ndev->n_targets];
rc = nci_add_new_protocol(ndev, target, ntf->rf_protocol,
@ -688,7 +710,7 @@ static int nci_rf_intf_activated_ntf_packet(struct nci_dev *ndev,
const struct sk_buff *skb)
{
struct nci_conn_info *conn_info;
struct nci_rf_intf_activated_ntf ntf;
struct nci_rf_intf_activated_ntf ntf = {};
const __u8 *data;
int err = NCI_STATUS_OK;
@ -806,12 +828,14 @@ static int nci_rf_intf_activated_ntf_packet(struct nci_dev *ndev,
switch (ntf.rf_interface) {
case NCI_RF_INTERFACE_ISO_DEP:
err = nci_extract_activation_params_iso_dep(ndev,
&ntf, data);
&ntf, data,
ntf.activation_params_len);
break;
case NCI_RF_INTERFACE_NFC_DEP:
err = nci_extract_activation_params_nfc_dep(ndev,
&ntf, data);
&ntf, data,
ntf.activation_params_len);
break;
case NCI_RF_INTERFACE_FRAME:

View File

@ -50,11 +50,27 @@ static u8 nci_core_init_rsp_packet_v1(struct nci_dev *ndev,
const struct nci_core_init_rsp_1 *rsp_1 = (void *)skb->data;
const struct nci_core_init_rsp_2 *rsp_2;
/* Ensure that the status field can be accessed. */
if (skb_headlen(skb) < 1)
return NCI_STATUS_SYNTAX_ERROR;
pr_debug("status 0x%x\n", rsp_1->status);
if (rsp_1->status != NCI_STATUS_OK)
return rsp_1->status;
/* Success response must contain the full fixed-size header */
if (skb_headlen(skb) < sizeof(*rsp_1))
return NCI_STATUS_SYNTAX_ERROR;
/* Ensure the variable-length rf_interfaces array and trailing
* rsp_2 structure are fully contained within the skb.
*/
if (skb_headlen(skb) < sizeof(*rsp_1) +
rsp_1->num_supported_rf_interfaces +
sizeof(*rsp_2))
return NCI_STATUS_SYNTAX_ERROR;
ndev->nfcc_features = __le32_to_cpu(rsp_1->nfcc_features);
ndev->num_supported_rf_interfaces = rsp_1->num_supported_rf_interfaces;
@ -87,15 +103,25 @@ static u8 nci_core_init_rsp_packet_v2(struct nci_dev *ndev,
const struct sk_buff *skb)
{
const struct nci_core_init_rsp_nci_ver2 *rsp = (void *)skb->data;
const u8 *supported_rf_interface = rsp->supported_rf_interfaces;
const u8 *supported_rf_interface;
u8 rf_interface_idx = 0;
u8 rf_extension_cnt = 0;
/* Ensure that the status field can be accessed. */
if (skb_headlen(skb) < 1)
return NCI_STATUS_SYNTAX_ERROR;
pr_debug("status %x\n", rsp->status);
if (rsp->status != NCI_STATUS_OK)
return rsp->status;
/* Success response must contain the full fixed-size header */
if (skb_headlen(skb) < sizeof(*rsp))
return NCI_STATUS_SYNTAX_ERROR;
supported_rf_interface = rsp->supported_rf_interfaces;
ndev->nfcc_features = __le32_to_cpu(rsp->nfcc_features);
ndev->num_supported_rf_interfaces = rsp->num_supported_rf_interfaces;
@ -104,13 +130,22 @@ static u8 nci_core_init_rsp_packet_v2(struct nci_dev *ndev,
NCI_MAX_SUPPORTED_RF_INTERFACES);
while (rf_interface_idx < ndev->num_supported_rf_interfaces) {
ndev->supported_rf_interfaces[rf_interface_idx++] = *supported_rf_interface++;
/* Each entry: [rf_interface_type (1B)] [ext_count (1B)] [ext...] */
if (supported_rf_interface + 2 > skb_tail_pointer(skb))
break;
ndev->supported_rf_interfaces[rf_interface_idx] = *supported_rf_interface++;
/* skip rf extension parameters */
rf_extension_cnt = *supported_rf_interface++;
if (supported_rf_interface + rf_extension_cnt > skb_tail_pointer(skb))
break;
/* Only count the entry after full validation */
rf_interface_idx++;
supported_rf_interface += rf_extension_cnt;
}
ndev->num_supported_rf_interfaces = rf_interface_idx;
ndev->max_logical_connections = rsp->max_logical_connections;
ndev->max_routing_table_size =
__le16_to_cpu(rsp->max_routing_table_size);
@ -336,6 +371,7 @@ static void nci_core_conn_close_rsp_packet(struct nci_dev *ndev,
list_del(&conn_info->list);
if (conn_info == ndev->rf_conn_info)
ndev->rf_conn_info = NULL;
devm_kfree(&ndev->nfc_dev->dev, conn_info->dest_params);
devm_kfree(&ndev->nfc_dev->dev, conn_info);
}
}