From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from mail-wm1-f45.google.com (mail-wm1-f45.google.com [209.85.128.45]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 7FD003812DA for ; Wed, 3 Jun 2026 13:59:39 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.128.45 ARC-Seal:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1780495186; cv=none; b=BUTdO6RQR1bl86DOzwFGBMl1dc5CsIyDCZMvw9WwCrMR6Y2MIw6oc0jrz2uaoGuGtKOXUj8iL7TZi/ePEeDiJjo8OoQ/kkzz2aN3m8dA/U75QsAhHTAmXYk93mT2F8becQdaxUY6QJstg57uhXxr6XFqyKj4pz1/tDcH5DQiRqU= ARC-Message-Signature:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1780495186; c=relaxed/simple; bh=yUcITEvuG4jog3LZnNkxt0FkhnQ5MYylhkYJGr0OQPo=; h=From:To:Cc:Subject:Date:Message-ID:MIME-Version; b=gd9jU1mdGGj3YsxEZ33TutY09vSmICWSiaB9Hf1wGMl5pc29W349RGtPPT/0Yh9yLw24fWRm8l4pyaDJ6MGbKAUKppgsewD5eL5OlmwdIz40vFbXjWw+d0VjVSm3elb9xI0DnWklqt+ks+t2zBqayY5d7eOMfX/Qg5KNu3o0rx4= ARC-Authentication-Results:i=1; smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=0sec.ai; spf=pass smtp.mailfrom=0sec.ai; dkim=temperror (0-bit key) header.d=0sec.ai header.i=@0sec.ai header.b=Cvj9VYEm; arc=none smtp.client-ip=209.85.128.45 Authentication-Results: smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=0sec.ai Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=0sec.ai Authentication-Results: smtp.subspace.kernel.org; dkim=temperror (0-bit key) header.d=0sec.ai header.i=@0sec.ai header.b="Cvj9VYEm" Received: by mail-wm1-f45.google.com with SMTP id 5b1f17b1804b1-49041e84237so7127305e9.1 for ; Wed, 03 Jun 2026 06:59:39 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=0sec.ai; s=google; t=1780495178; x=1781099978; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:message-id:date:subject:cc :to:from:from:to:cc:subject:date:message-id:reply-to; bh=1p4OnpYDXMpkwZDQAdqQJhqxfh5Hb6QqekAS/cW1P68=; b=Cvj9VYEmOJklKHEkioLtqTMoNp6U/WLregPyGcObvHl9/EtS5xXKf9bb0/Udj9Uy/L WZiNk/Jh+Tk5PEiTiQmcsG4Xt0wabo4+HHcTBTiMNuAkiMHUvTg8h5J3b2pWu4xdLzrX aA2wp/gQviyMWm8lnNVkKokp6UpA25Pn1ViKP8rksLsfgT7YybrTRdFC/pIr+c999HsD gQXUwgiLYrcqxn28V9xbue8VU2K2zoJlV8m8lS2jA5fDcY8kgg8f+KJOYSCigfitBp4g k6vJVVtI+qYSfeVl/y2JdKUC4Jep+0kFHpAannyJL0l22ar+ScthTWfsbIChrVJN3SEq pJ/g== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1780495178; x=1781099978; h=content-transfer-encoding:mime-version:message-id:date:subject:cc :to:from:x-gm-gg:x-gm-message-state:from:to:cc:subject:date :message-id:reply-to; bh=1p4OnpYDXMpkwZDQAdqQJhqxfh5Hb6QqekAS/cW1P68=; b=krKIPEi8+JrBNt0wkYrA7t8JEHK0geXdAriJyGjgJZyS8uAWs6QLR179QrDbxCUZcE lCMpOBm6VmQP+nvY/TE0jCcC0Gl5bxXhl9RuRl/95p0ICdMErBaw8I/Tx/l7PxIdW8tU YI+cBx+FGpx9ecvSpRUf9q/DzxuhYMajarLWul9uIncV4AXtHDmX08ULYJxZpVZquHZT pgD0PEtVf42w3VwKTkgGObJu0Kp2wMVNX7IxKQ0uOWjXh34o2JCs7X+1wAiGLl1AaO0r 3/Fhms3kSmcU1D4f3UA5T6x7COjffoAwBFwILBM1/jIB7wPvCQbBn63cR89k9cyZLnhx VYYA== X-Forwarded-Encrypted: i=1; AFNElJ/yd1uR1V6QJGuP7weT9o6fU0r74Y6Luktcep4FZhTbnQw0XdJ5k/GtId6caDxl49fLzVp6u8ymwZVYxCg=@vger.kernel.org X-Gm-Message-State: AOJu0YyuciwNHF1Nmm/bxRqYg3MmgfxZzAIjSLMfVA+mfOFnabsH0SOt CxrPwb+q5igGyTE4ZM2EBceAhmP2+AHohRWc8vgGeDlB3B/bAW/JBQPDy9KqFKMaATvQloXbOAN ibjJg1N4MKgw= X-Gm-Gg: Acq92OGG3VWShXUzLPlcxPkF9TojUT7kD4E8Bmi1VXeLhl2n+9kSwZtpd3zM0ol6BBv aOUw4RzBVM1fQ5v7e3DPI+Xxuv34K1oeRWYdi7rM6HDKYZokWVHJGhrZ/ArBpuWlkOxVNJsxys1 Dp5lTEiJ56xH6NENn6cLZRPZWWyHGQqiZYFXoqlfG8jC1KECYYW5ql1fUiL9ywzWqnbr2PRa81R PV9Zob8XFBkOHhaUh2JeJZqVi2w0W0uyF8f9+4gdwl2Tm96KlENKxlcgWxjeb7EfspaAQzSIfw8 FmoWT3hA49CQsXCSlkZyp1CTEo9QOdqklr6ug0cvKpGt6k/5p/sJpAPfhCbpcSvL2m63S5/Ejco TWIdeNr0qj9hgTTpYvdD0O8rxqmp5N/GSBLCQfKilq6+Ze44WdWhn3Sagcyp8T32VFuWBADSXvW VEVTC0S/YGDmD5rQXAQv61wzTr5Sr9OSGyEIivyO4G/RypnL6gSuZO63E9flNCWUPUnCVIZqQMb tJuXlH3/ge4rrpVWBfaDeNSWyQFEBX/ZbTwppH2NHC365Sy1YRgxXxcAHG9YIRRdg== X-Received: by 2002:a05:600c:3e19:b0:490:8b0b:d3b1 with SMTP id 5b1f17b1804b1-490b614c9demr55616715e9.12.1780495177808; Wed, 03 Jun 2026 06:59:37 -0700 (PDT) Received: from PeakBook-Mini.tail8e484.ts.net ([178.197.223.24]) by smtp.gmail.com with ESMTPSA id 5b1f17b1804b1-490b7a7cb85sm59455345e9.1.2026.06.03.06.59.36 (version=TLS1_3 cipher=TLS_CHACHA20_POLY1305_SHA256 bits=256/256); Wed, 03 Jun 2026 06:59:37 -0700 (PDT) From: Doruk Tan Ozturk To: David Heidelberg , oe-linux-nfc@lists.linux.dev Cc: David Laight , Simon Horman , netdev@vger.kernel.org, linux-kernel@vger.kernel.org, Doruk Tan Ozturk Subject: [PATCH net v2] nfc: llcp: bound SNL TLV parsing to the skb and add length checks Date: Wed, 3 Jun 2026 15:59:35 +0200 Message-ID: <20260603135935.62647-1-doruk@0sec.ai> X-Mailer: git-send-email 2.53.0 Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: 8bit nfc_llcp_recv_snl() walked the SNL TLV list using a u16 offset/length pair derived from skb->len, without bounding reads to the actual skb data. Three problems followed: - For a short frame (skb->len < LLCP_HEADER_SIZE), tlv_len underflowed. - The per-TLV header (type, length) was read without checking that two bytes remained. - A declared TLV length could run past the end of the buffer, and an SDREQ with length == 0 made "service_name_len = length - 1" underflow to SIZE_MAX, driving an out-of-bounds read in the following strncmp() / nfc_llcp_sock_from_sn(). The SDRES case likewise read tlv[2]/tlv[3] without a length check. A nearby NFC device can reach this without authentication; LLCP link activation happens automatically after NFC-DEP. Walk the TLV list by pointer, bounded by skb_tail_pointer(), and validate each TLV declared length before use. Add explicit length checks for SDREQ (>= 1) and SDRES (>= 2). Found by 0sec automated security-research tooling (https://0sec.ai). Signed-off-by: Doruk Tan Ozturk --- v2: - Walk by pointer bounded on skb_tail_pointer(); drop the 16-bit offset/tlv_len math and fix the short-frame underflow (David Laight). - Add an SDRES length >= 2 check alongside SDREQ length >= 1 (David Laight). - Bound the SDREQ service-name pr_debug to the field length (no over-read). - Rebased onto linux-nfc for-next (David Heidelberg). net/nfc/llcp_core.c | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/net/nfc/llcp_core.c b/net/nfc/llcp_core.c index dc65c719f..d23ca815a 100644 --- a/net/nfc/llcp_core.c +++ b/net/nfc/llcp_core.c @@ -1286,8 +1286,7 @@ 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; struct nfc_llcp_sdp_tlv *sdp; @@ -1306,21 +1305,28 @@ static void nfc_llcp_recv_snl(struct nfc_llcp_local *local, } 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", + (int)min_t(size_t, service_name_len, 16), + service_name); if (service_name_len == strlen("urn:nfc:sn:sdp") && !strncmp(service_name, "urn:nfc:sn:sdp", @@ -1380,6 +1386,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 +1417,6 @@ static void nfc_llcp_recv_snl(struct nfc_llcp_local *local, break; } - offset += length + 2; tlv += length + 2; } -- 2.53.0