mirror of
https://github.com/torvalds/linux.git
synced 2026-09-22 04:34:03 +02:00
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:
commit
a22054d743
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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 */
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user