* Re: [Security] NFC: digital: peer-controlled stack overflow in digital_in_recv_sensf_res() (sibling to CVE-2026-31622)
[not found] <CAFqytU96TsqxZpcEtotFG2bP3kr=zwDQz94MonKYmSSL12rbWw@mail.gmail.com>
@ 2026-04-28 12:21 ` Krzysztof Kozlowski
2026-04-28 12:34 ` Krzysztof Kozlowski
0 siblings, 1 reply; 3+ messages in thread
From: Krzysztof Kozlowski @ 2026-04-28 12:21 UTC (permalink / raw)
To: G, Networking, Jakub Kicinski, David Heidelberg
On 28/04/2026 14:05, G wrote:
> Hello,
>
> I am reporting a peer-controlled stack out-of-bounds write in
> net/nfc/digital_technology.c::digital_in_recv_sensf_res(). The handler
> copies up to ~250 attacker-controlled bytes into the 18-byte stack array
> target.sensf_res of a stack-local struct nfc_target. A malicious NFC-F
> peer (or NFC-F card emulator) within RF range of a Linux device that is
> polling FELICA via a digital-framework driver triggers the bug with a
> single oversized SENSF response.
>
> The bug is the missed sibling of CVE-2026-31622 (HIGH 8.8), which fixed
Referring to CVE only obfuscates the process and makes it less credible,
you should refer to the actual commit.
> the analogous NFC-A cascade overflow in digital_in_recv_sdd_res() in the
> same file. That fix is present in current trees (verified at
> net/nfc/digital_technology.c:427); the SENSF handler was overlooked by
> that audit round and remains unfixed at the time of this report.
>
> I have a verified end-to-end PoC; the kernel BUG was reproduced on stock
> Ubuntu 6.17.0-22-generic with FORTIFY_SOURCE=y, STACKPROTECTOR_STRONG=y,
> KASLR=y, KASAN=n.
>
> ================================================================
> Affected kernel version range
> ================================================================
>
> - Verified vulnerable: Linux 7.0.1 stable
> (net/nfc/digital_technology.c:748-800), with end-to-end
> reproduction performed on Linux 6.17.0-22-generic (Ubuntu).
With which driver? The virtual testing one?
> - The function body is the same as in current upstream stable
> releases inspected by the reporter; bisecting the original
> introduction is left to the maintainer (the reporter does not
> have a local clone of the full history).
Heh, if you don't want to be judged as sending us LLM slop, I really
suggest to avoid LLM slop products, like speaking about yourself in 3rd
person tense. For me it's an obvious sign.
This should be sent to the mailing lists to all people. Sending this to
me suggests it is some duplicated posting.
> - Sibling NFC handlers in the same file (sens_res, sdd_res,
> sensb_res, attrib_res, iso15693_inv_res) already use bounded
> copies. The NFC-DEP atr_res in net/nfc/digital_dep.c uses a
> sizeof-based length validation. Only sensf_res lacks an
> upper-bound check.
>
> ================================================================
> Description
> ================================================================
>
> digital_in_recv_sensf_res() validates only the minimum length of the
> incoming SENSF response and then copies resp->len bytes into the
> fixed-size target.sensf_res field of a stack-local struct nfc_target:
>
> static void digital_in_recv_sensf_res(struct nfc_digital_dev *ddev,
> void *arg, struct sk_buff *resp)
> {
> struct nfc_target target; /* <-- STACK */
> ...
> if (resp->len < DIGITAL_SENSF_RES_MIN_LENGTH) { /* MIN-only */
> rc = -EIO;
> goto exit;
> }
>
> if (!DIGITAL_DRV_CAPS_IN_CRC(ddev))
> digital_skb_check_crc_f(resp); /* trims CRC if SW */
>
> skb_pull(resp, 1); /* -1 byte */
>
> memset(&target, 0, sizeof(struct nfc_target));
> sensf_res = (struct digital_sensf_res *)resp->data;
>
> memcpy(target.sensf_res, sensf_res, resp->len); /* BUG */
> target.sensf_res_len = resp->len;
> ...
> }
>
> target.sensf_res has fixed size NFC_SENSF_RES_MAXSIZE == 18
> (include/uapi/linux/nfc.h:222). resp->len is peer-controlled and
> limited only by the on-wire NFC-F LEN byte (1 byte, ~250 bytes
> attacker-controllable after the skb_pull and the optional CRC trim).
>
> Reachability:
>
> peer NFC-F responds to SENSF_REQ
> -> controller driver (in-tree drivers using
> nfc_digital_allocate_device(): trf7970a, port100, st95hf,
> nfcsim)
> -> nfc_digital RX path
> -> digital_send_cmd_complete (workqueue)
> -> digital_wq_cmd_complete
> -> cmd->cmd_cb(...)
> == digital_in_recv_sensf_res(ddev, NULL, resp) <-- BUG
>
> ================================================================
> Conditions
> ================================================================
>
> - CONFIG_NFC=y or =m
> - CONFIG_NFC_DIGITAL=y or =m
> - A controller driver registered via nfc_digital_allocate_device()
> (i.e. uses the digital framework, not pure NCI).
> - User space has started a FELICA / NFC-DEP poll (typically neard).
> - Adjacent attacker (peer NFC-F device or card emulator within RF
> range, ~10 cm), or a compromised NFC controller transport.
>
> NCI-only NFC stacks (controllers that present an NCI interface directly
> to the kernel, e.g. nxp-nci, s3fwrn5, the NCI paths of pn544/st-nci, and
> virtual_ncidev) do not reach this code path.
So you want to say there is no single driver affected by this, right?
>
> ================================================================
> Reproducer
> ================================================================
>
> A self-contained out-of-tree kernel module + Python netlink trigger is
> attached as a tarball. It registers a fake nfc_digital_dev advertising
> NFC_PROTO_FELICA_MASK and NFC_DIGITAL_DRV_CAPS_IN_CRC; its ->in_send_cmd
> op intercepts the framework-issued SENSF_REQ and invokes the
> framework-supplied complete callback (digital_send_cmd_complete) with a
> crafted oversized SENSF response. No NFC hardware is required; the
> same skb shape comes off the wire from any NFC-F-capable peer.
>
> Build and run on the test VM:
>
> $ make
> $ sudo modprobe nfc nfc_digital
> $ sudo insmod ./poc.ko evil_len=200
> $ sudo python3 ./trigger.py
> $ sudo dmesg | grep -E -A 30 'fortify|BUG|digital_in_recv_sensf'
>
> Verified observation on Linux 6.17.0-22-generic (Ubuntu, default config,
> FORTIFY_SOURCE=y, STACKPROTECTOR_STRONG=y, KASLR=y, KASAN=n):
>
> poc: delivering evil SENSF_RES, skb->len=200, effective memcpy=199
> bytes into 18-byte target.sensf_res
> DIGITAL TX: 06 00 ff ff 00 00
> DIGITAL RX: c8 01 41 41 41 41 41 41 41 41 41 41 41 41 41 41 ...
>
> ------------[ cut here ]------------
> memcpy: detected buffer overflow: 199 byte write of buffer size 51
> WARNING: CPU: 0 PID: 515676 at lib/string_helpers.c:1035
> __fortify_report+0x64/0xc8
> Workqueue: events digital_wq_cmd_complete [nfc_digital]
> Call trace:
> __fortify_report+0x64/0xc8 (P)
> __fortify_panic+0x14/0x18
> digital_in_recv_sensf_res+0x26c/0x280 [nfc_digital]
> digital_wq_cmd_complete+0x80/0x158 [nfc_digital]
> process_one_work+0x174/0x428
> worker_thread+0x310/0x440
> kthread+0x110/0x130
> ret_from_fork+0x10/0x20
>
> ------------[ cut here ]------------
> kernel BUG at lib/string_helpers.c:1043!
> Internal error: Oops - BUG: 00000000f2000800 [#1] SMP
> [same trace through __fortify_panic -> digital_in_recv_sensf_res+0x26c]
> note: kworker/0:0[515676] exited with irqs disabled
> note: kworker/0:0[515676] exited with preempt_count 1
>
> The full dmesg is attached as observed-dmesg.log.
>
> ================================================================
> On the FORTIFY "buffer size 51" message
> ================================================================
>
> __builtin_object_size(target.sensf_res, 0) on a struct member returns
> the bytes from that member to the end of the enclosing object. In
> struct nfc_target, the byte count from sensf_res[18] to the end is
>
> 18 (sensf_res) + 1 (hci_reader_gate) + 1 (logical_idx)
> + 1 (is_iso15693) + 1 (iso15693_dsfid) + 8 (iso15693_uid)
> + 1 (ats_len) + 20 (ats) = 51
>
> so FORTIFY catches writes > 51 bytes only. Writes in the range [19, 51]
> silently corrupt the adjacent fields on FORTIFY=y builds; those values
> propagate into nfc_dev->targets[] via digital_target_found() ->
> nfc_targets_found() and reach user space as NFC_EVENT_TARGETS_FOUND
> attributes. This means the bug has two distinct effective severities
> even on hardened distro builds:
>
> Response length (post-pull) Outcome on FORTIFY=y, STACKPROTECTOR=y
> -----------------------------+----------------------------------------
> <= 18 | within bounds, no bug
> 19 - 51 | silent corruption of nfc_target fields
> | after sensf_res; values reach user
> | space via netlink
> 52 - ~250 | __fortify_panic BUG -> kernel oops /
> | DoS
>
> ================================================================
> Suspected location and analysis
> ================================================================
>
> - Bug site: net/nfc/digital_technology.c:781
> - Bug class: CWE-787 (Out-of-bounds Write), CWE-121 (Stack-based
> Buffer Overflow).
> - Severity: pre-auth, AV:A. CVSS 3.1 baseline (pure DoS, the
> confirmed outcome on every hardened build):
> AV:A/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H = 6.5 (MEDIUM)
If there is no driver, severity is LOW.
> If the [19, 51] silent corruption surfacing via
> NFC_EVENT_TARGETS_FOUND netlink events is counted as integrity
> impact, the vector becomes
> AV:A/AC:L/PR:N/UI:N/S:U/C:N/I:L/A:H = 7.1 (HIGH)
> Upgrade path to ~8.8 (matching the precedent CVE-2026-31622)
> only on builds without FORTIFY_SOURCE / STACKPROTECTOR if RCE
> is demonstrated; we have not constructed that exploit. This
> is NOT a local privilege escalation primitive.
> - Sibling audit (verified by inspection):
> digital_in_recv_sens_res (digital_technology.c:482)
> resp->len < sizeof(u16) check; reads u16, no
> peer-controlled memcpy into target OK
> digital_in_recv_sdd_res (digital_technology.c:387)
> target->nfcid1_len + size > NFC_NFCID1_MAXSIZE
> cap before memcpy at line 427 (CVE-2026-31622
> fix) OK
> digital_in_recv_sensb_res (digital_technology.c:651)
> resp->len != sizeof(*sensb_res) strict size
> match; no peer-controlled memcpy into target OK
> digital_in_recv_attrib_res (digital_technology.c:581)
> resp->len < sizeof(*attrib_res); reads fields
> only OK
> digital_in_recv_iso15693_inv_res (digital_technology.c:843)
> resp->len != sizeof(*res); memcpy into
> target->iso15693_uid is dest-bound OK
> digital_in_recv_atr_res (digital_dep.c:399)
> resp->len < sizeof(*atr_res); remaining bytes
> go via nfc_set_remote_general_bytes (separate
> validation, not direct-memcpy pattern) OK
> digital_in_recv_sensf_res (digital_technology.c:748)
> MIN-only check; no MAX cap before memcpy at
> line 781 BUG
> digital_in_recv_sensf_res() is the lone outlier in the
> peer-controlled-length-to-nfc_target-memcpy pattern audited
> here.
>
> ================================================================
> Proposed fix
> ================================================================
>
> Add a maximum-length check, mirroring the bounded pattern already used
> in every sibling handler in the same file:
>
> --- a/net/nfc/digital_technology.c
> +++ b/net/nfc/digital_technology.c
> @@ -762,7 +762,12 @@ static void digital_in_recv_sensf_res(struct
> nfc_digital_dev *ddev, void *arg,
> if (resp->len < DIGITAL_SENSF_RES_MIN_LENGTH) {
> rc = -EIO;
> goto exit;
> }
>
> + if (resp->len > NFC_SENSF_RES_MAXSIZE) {
> + rc = -EIO;
> + goto exit;
> + }
> +
> if (!DIGITAL_DRV_CAPS_IN_CRC(ddev)) {
> rc = digital_skb_check_crc_f(resp);
>
> Signed-off-by: <your name and email here>
Do you want to send a proper Linux patch for it with proper authorship
(see submitting patches)?
Best regards,
Krzysztof
^ permalink raw reply [flat|nested] 3+ messages in thread