Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

net: nfc: nci: Add parameter validation for packet data

Syzbot reported an uninitialized value bug in nci_init_req, which was
introduced by commit 5aca7966d2a7 ("Merge tag
'perf-tools-fixes-for-v6.17-2025-09-16' of
git://git.kernel.org/pub/scm/linux/kernel/git/perf/perf-tools").

This bug arises due to very limited and poor input validation
that was done at nic_valid_size(). This validation only
validates the skb->len (directly reflects size provided at the
userspace interface) with the length provided in the buffer
itself (interpreted as NCI_HEADER). This leads to the processing
of memory content at the address assuming the correct layout
per what opcode requires there. This leads to the accesses to
buffer of `skb_buff->data` which is not assigned anything yet.

Following the same silent drop of packets of invalid sizes at
`nic_valid_size()`, add validation of the data in the respective
handlers and return error values in case of failure. Release
the skb if error values are returned from handlers in
`nci_nft_packet` and effectively do a silent drop

Possible TODO: because we silently drop the packets, the
call to `nci_request` will be waiting for completion of request
and will face timeouts. These timeouts can get excessively logged
in the dmesg. A proper handling of them may require to export
`nci_request_cancel` (or propagate error handling from the
nft packets handlers).

Reported-by: syzbot+740e04c2a93467a0f8c8@syzkaller.appspotmail.com
Closes: https://syzkaller.appspot.com/bug?extid=740e04c2a93467a0f8c8
Fixes: 6a2968aaf50c ("NFC: basic NCI protocol implementation")
Tested-by: syzbot+740e04c2a93467a0f8c8@syzkaller.appspotmail.com
Cc: stable@vger.kernel.org
Signed-off-by: Deepak Sharma <deepak.sharma.472935@gmail.com>
Reviewed-by: Vadim Fedorenko <vadim.fedorenko@linux.dev>
Link: https://patch.msgid.link/20250925132846.213425-1-deepak.sharma.472935@gmail.com
Signed-off-by: Paolo Abeni <pabeni@redhat.com>

authored by

Deepak Sharma and committed by
Paolo Abeni
9c328f54 99e4c35e

+99 -36
+99 -36
net/nfc/nci/ntf.c
··· 27 27 28 28 /* Handle NCI Notification packets */ 29 29 30 - static void nci_core_reset_ntf_packet(struct nci_dev *ndev, 31 - const struct sk_buff *skb) 30 + static int nci_core_reset_ntf_packet(struct nci_dev *ndev, 31 + const struct sk_buff *skb) 32 32 { 33 33 /* Handle NCI 2.x core reset notification */ 34 - const struct nci_core_reset_ntf *ntf = (void *)skb->data; 34 + const struct nci_core_reset_ntf *ntf; 35 + 36 + if (skb->len < sizeof(struct nci_core_reset_ntf)) 37 + return -EINVAL; 38 + 39 + ntf = (struct nci_core_reset_ntf *)skb->data; 35 40 36 41 ndev->nci_ver = ntf->nci_ver; 37 42 pr_debug("nci_ver 0x%x, config_status 0x%x\n", ··· 47 42 __le32_to_cpu(ntf->manufact_specific_info); 48 43 49 44 nci_req_complete(ndev, NCI_STATUS_OK); 45 + 46 + return 0; 50 47 } 51 48 52 - static void nci_core_conn_credits_ntf_packet(struct nci_dev *ndev, 53 - struct sk_buff *skb) 49 + static int nci_core_conn_credits_ntf_packet(struct nci_dev *ndev, 50 + struct sk_buff *skb) 54 51 { 55 - struct nci_core_conn_credit_ntf *ntf = (void *) skb->data; 52 + struct nci_core_conn_credit_ntf *ntf; 56 53 struct nci_conn_info *conn_info; 57 54 int i; 55 + 56 + if (skb->len < sizeof(struct nci_core_conn_credit_ntf)) 57 + return -EINVAL; 58 + 59 + ntf = (struct nci_core_conn_credit_ntf *)skb->data; 58 60 59 61 pr_debug("num_entries %d\n", ntf->num_entries); 60 62 ··· 80 68 conn_info = nci_get_conn_info_by_conn_id(ndev, 81 69 ntf->conn_entries[i].conn_id); 82 70 if (!conn_info) 83 - return; 71 + return 0; 84 72 85 73 atomic_add(ntf->conn_entries[i].credits, 86 74 &conn_info->credits_cnt); ··· 89 77 /* trigger the next tx */ 90 78 if (!skb_queue_empty(&ndev->tx_q)) 91 79 queue_work(ndev->tx_wq, &ndev->tx_work); 80 + 81 + return 0; 92 82 } 93 83 94 - static void nci_core_generic_error_ntf_packet(struct nci_dev *ndev, 95 - const struct sk_buff *skb) 84 + static int nci_core_generic_error_ntf_packet(struct nci_dev *ndev, 85 + const struct sk_buff *skb) 96 86 { 97 - __u8 status = skb->data[0]; 87 + __u8 status; 88 + 89 + if (skb->len < 1) 90 + return -EINVAL; 91 + 92 + status = skb->data[0]; 98 93 99 94 pr_debug("status 0x%x\n", status); 100 95 ··· 110 91 (the state remains the same) */ 111 92 nci_req_complete(ndev, status); 112 93 } 94 + 95 + return 0; 113 96 } 114 97 115 - static void nci_core_conn_intf_error_ntf_packet(struct nci_dev *ndev, 116 - struct sk_buff *skb) 98 + static int nci_core_conn_intf_error_ntf_packet(struct nci_dev *ndev, 99 + struct sk_buff *skb) 117 100 { 118 - struct nci_core_intf_error_ntf *ntf = (void *) skb->data; 101 + struct nci_core_intf_error_ntf *ntf; 102 + 103 + if (skb->len < sizeof(struct nci_core_intf_error_ntf)) 104 + return -EINVAL; 105 + 106 + ntf = (struct nci_core_intf_error_ntf *)skb->data; 119 107 120 108 ntf->conn_id = nci_conn_id(&ntf->conn_id); 121 109 ··· 131 105 /* complete the data exchange transaction, if exists */ 132 106 if (test_bit(NCI_DATA_EXCHANGE, &ndev->flags)) 133 107 nci_data_exchange_complete(ndev, NULL, ntf->conn_id, -EIO); 108 + 109 + return 0; 134 110 } 135 111 136 112 static const __u8 * ··· 357 329 ndev->n_targets = 0; 358 330 } 359 331 360 - static void nci_rf_discover_ntf_packet(struct nci_dev *ndev, 361 - const struct sk_buff *skb) 332 + static int nci_rf_discover_ntf_packet(struct nci_dev *ndev, 333 + const struct sk_buff *skb) 362 334 { 363 335 struct nci_rf_discover_ntf ntf; 364 - const __u8 *data = skb->data; 336 + const __u8 *data; 365 337 bool add_target = true; 338 + 339 + if (skb->len < sizeof(struct nci_rf_discover_ntf)) 340 + return -EINVAL; 341 + 342 + data = skb->data; 366 343 367 344 ntf.rf_discovery_id = *data++; 368 345 ntf.rf_protocol = *data++; ··· 423 390 nfc_targets_found(ndev->nfc_dev, ndev->targets, 424 391 ndev->n_targets); 425 392 } 393 + 394 + return 0; 426 395 } 427 396 428 397 static int nci_extract_activation_params_iso_dep(struct nci_dev *ndev, ··· 588 553 return NCI_STATUS_OK; 589 554 } 590 555 591 - static void nci_rf_intf_activated_ntf_packet(struct nci_dev *ndev, 592 - const struct sk_buff *skb) 556 + static int nci_rf_intf_activated_ntf_packet(struct nci_dev *ndev, 557 + const struct sk_buff *skb) 593 558 { 594 559 struct nci_conn_info *conn_info; 595 560 struct nci_rf_intf_activated_ntf ntf; 596 - const __u8 *data = skb->data; 561 + const __u8 *data; 597 562 int err = NCI_STATUS_OK; 563 + 564 + if (skb->len < sizeof(struct nci_rf_intf_activated_ntf)) 565 + return -EINVAL; 566 + 567 + data = skb->data; 598 568 599 569 ntf.rf_discovery_id = *data++; 600 570 ntf.rf_interface = *data++; ··· 707 667 if (err == NCI_STATUS_OK) { 708 668 conn_info = ndev->rf_conn_info; 709 669 if (!conn_info) 710 - return; 670 + return 0; 711 671 712 672 conn_info->max_pkt_payload_len = ntf.max_data_pkt_payload_size; 713 673 conn_info->initial_num_credits = ntf.initial_num_credits; ··· 761 721 pr_err("error when signaling tm activation\n"); 762 722 } 763 723 } 724 + 725 + return 0; 764 726 } 765 727 766 - static void nci_rf_deactivate_ntf_packet(struct nci_dev *ndev, 767 - const struct sk_buff *skb) 728 + static int nci_rf_deactivate_ntf_packet(struct nci_dev *ndev, 729 + const struct sk_buff *skb) 768 730 { 769 731 const struct nci_conn_info *conn_info; 770 - const struct nci_rf_deactivate_ntf *ntf = (void *)skb->data; 732 + const struct nci_rf_deactivate_ntf *ntf; 733 + 734 + if (skb->len < sizeof(struct nci_rf_deactivate_ntf)) 735 + return -EINVAL; 736 + 737 + ntf = (struct nci_rf_deactivate_ntf *)skb->data; 771 738 772 739 pr_debug("entry, type 0x%x, reason 0x%x\n", ntf->type, ntf->reason); 773 740 774 741 conn_info = ndev->rf_conn_info; 775 742 if (!conn_info) 776 - return; 743 + return 0; 777 744 778 745 /* drop tx data queue */ 779 746 skb_queue_purge(&ndev->tx_q); ··· 812 765 } 813 766 814 767 nci_req_complete(ndev, NCI_STATUS_OK); 768 + 769 + return 0; 815 770 } 816 771 817 - static void nci_nfcee_discover_ntf_packet(struct nci_dev *ndev, 818 - const struct sk_buff *skb) 772 + static int nci_nfcee_discover_ntf_packet(struct nci_dev *ndev, 773 + const struct sk_buff *skb) 819 774 { 820 775 u8 status = NCI_STATUS_OK; 821 - const struct nci_nfcee_discover_ntf *nfcee_ntf = 822 - (struct nci_nfcee_discover_ntf *)skb->data; 776 + const struct nci_nfcee_discover_ntf *nfcee_ntf; 777 + 778 + if (skb->len < sizeof(struct nci_nfcee_discover_ntf)) 779 + return -EINVAL; 780 + 781 + nfcee_ntf = (struct nci_nfcee_discover_ntf *)skb->data; 823 782 824 783 /* NFCForum NCI 9.2.1 HCI Network Specific Handling 825 784 * If the NFCC supports the HCI Network, it SHALL return one, ··· 836 783 ndev->cur_params.id = nfcee_ntf->nfcee_id; 837 784 838 785 nci_req_complete(ndev, status); 786 + 787 + return 0; 839 788 } 840 789 841 790 void nci_ntf_packet(struct nci_dev *ndev, struct sk_buff *skb) ··· 864 809 865 810 switch (ntf_opcode) { 866 811 case NCI_OP_CORE_RESET_NTF: 867 - nci_core_reset_ntf_packet(ndev, skb); 812 + if (nci_core_reset_ntf_packet(ndev, skb)) 813 + goto end; 868 814 break; 869 815 870 816 case NCI_OP_CORE_CONN_CREDITS_NTF: 871 - nci_core_conn_credits_ntf_packet(ndev, skb); 817 + if (nci_core_conn_credits_ntf_packet(ndev, skb)) 818 + goto end; 872 819 break; 873 820 874 821 case NCI_OP_CORE_GENERIC_ERROR_NTF: 875 - nci_core_generic_error_ntf_packet(ndev, skb); 822 + if (nci_core_generic_error_ntf_packet(ndev, skb)) 823 + goto end; 876 824 break; 877 825 878 826 case NCI_OP_CORE_INTF_ERROR_NTF: 879 - nci_core_conn_intf_error_ntf_packet(ndev, skb); 827 + if (nci_core_conn_intf_error_ntf_packet(ndev, skb)) 828 + goto end; 880 829 break; 881 830 882 831 case NCI_OP_RF_DISCOVER_NTF: 883 - nci_rf_discover_ntf_packet(ndev, skb); 832 + if (nci_rf_discover_ntf_packet(ndev, skb)) 833 + goto end; 884 834 break; 885 835 886 836 case NCI_OP_RF_INTF_ACTIVATED_NTF: 887 - nci_rf_intf_activated_ntf_packet(ndev, skb); 837 + if (nci_rf_intf_activated_ntf_packet(ndev, skb)) 838 + goto end; 888 839 break; 889 840 890 841 case NCI_OP_RF_DEACTIVATE_NTF: 891 - nci_rf_deactivate_ntf_packet(ndev, skb); 842 + if (nci_rf_deactivate_ntf_packet(ndev, skb)) 843 + goto end; 892 844 break; 893 845 894 846 case NCI_OP_NFCEE_DISCOVER_NTF: 895 - nci_nfcee_discover_ntf_packet(ndev, skb); 847 + if (nci_nfcee_discover_ntf_packet(ndev, skb)) 848 + goto end; 896 849 break; 897 850 898 851 case NCI_OP_RF_NFCEE_ACTION_NTF: