digital_recv_dep_data_gather() allocates chaining_skb with
nfc_alloc_recv_skb(), which only reserves 1 byte of headroom.
Once the chained payload is fully gathered, it is passed to
digital_tg_send_dep_res() or digital_in_send_dep_req(), both of
which push a 3-byte DEP header plus up to 2 bytes of SoD, needing
at least 5 bytes of headroom.
skb_push() unconditionally calls skb_under_panic() when headroom is
insufficient, so this is a guaranteed kernel BUG. A remote NFC peer
can trigger it in target mode by sending MI-flagged DEP_REQ fragments
followed by an ACK with matching PNI, or in initiator mode via the
symmetric chaining ACK path. Normal DEP frames use digital_skb_alloc()
which gets headroom from ddev->tx_headroom, so the bug only manifests
through the chaining_skb path.
The skb_copy_expand() reallocation in the same function preserves the
original 1-byte headroom and does not add tailroom for the CRC or DID
byte appended by the send path.
Switch to digital_skb_alloc() which reserves the same headroom and
tailroom that every other send buffer in this file uses, and pass
ddev->tx_headroom and ddev->tx_tailroom to skb_copy_expand().
Fixes: c12715ab3f01 ("NFC: digital: Add NFC-DEP Receive Chaining Support")
Cc: stable@vger.kernel.org
Signed-off-by: Liu Chao <redacted>
---
net/nfc/digital_dep.c | 10 +++++-----
1 file changed, 5 insertions(+), 5 deletions(-)
diff --git a/net/nfc/digital_dep.c b/net/nfc/digital_dep.c
index 3982fa084..6d8e662a3 100644
--- a/net/nfc/digital_dep.c
+++ b/net/nfc/digital_dep.c
@@ -240,8 +240,8 @@ digital_recv_dep_data_gather(struct nfc_digital_dev *ddev, u8 pfb,
if (DIGITAL_NFC_DEP_MI_BIT_SET(pfb) && (!ddev->chaining_skb)) {
ddev->chaining_skb =
- nfc_alloc_recv_skb(8 * ddev->local_payload_max,
- GFP_KERNEL);
+ digital_skb_alloc(ddev,
+ 8 * ddev->local_payload_max);
if (!ddev->chaining_skb) {
rc = -ENOMEM;
goto error;@@ -251,9 +251,9 @@ digital_recv_dep_data_gather(struct nfc_digital_dev *ddev, u8 pfb,
if (ddev->chaining_skb) {
if (resp->len > skb_tailroom(ddev->chaining_skb)) {
new_skb = skb_copy_expand(ddev->chaining_skb,
- skb_headroom(
- ddev->chaining_skb),
- 8 * ddev->local_payload_max,
+ ddev->tx_headroom,
+ 8 * ddev->local_payload_max +
+ ddev->tx_tailroom,
GFP_KERNEL);
if (!new_skb) {
rc = -ENOMEM;--
2.50.1