From: Simon Horman <horms@kernel.org>
To: Chuyf26 <Chuyf26@linux.alibaba.com>
Cc: Dmitry Kozlov <xeb@mail.ru>, netdev@vger.kernel.org
Subject: Re: [PATCH] pptp: reject payloads shorter than the PPP protocol field
Date: Fri, 21 Aug 2026 10:26:18 +0100 [thread overview]
Message-ID: <20260821092618.GF265046@horms.kernel.org> (raw)
In-Reply-To: <20260818162143.gn-j15t2twSq3EK4tluniorb9JMdD9cS6_NWtWioiRc@z>
This is an AI-generated review of your patch. The human sending this
email has considered the AI review valid, or at least plausible.
Full review at: https://netdev-ai.bots.linux.dev/sashiko/#/patchset/20260818162229.yOFI43Vfo0v911uVw0AFpyHQ_aOe_fsjoQXPZP70vMI@z?part=1
Simon says:
Given the first issue raised by the AI-generated review below, it seems to
me that this is more of a hardening patch - for net-next, no Fixes tag -
than a bug fix - for net, with a Fixes tag.
---
--- Patch [1]: [PATCH] x25: validate class D facility length before reading it ---
commit 37de2e4212c8268079bc40f17b99b1685565a4da
Author: Chuyf26 <Chuyf26@linux.alibaba.com>
x25: validate class D facility length before reading it
The patch adds a len < 2 test to the X25_FAC_CLASS_D case of
x25_parse_facilities() so that the two facility header bytes are known to
be present before p[1] is loaded. Classes A, B and C already validate
their fixed lengths in the same way.
No Link: tags are present in the commit header.
> x25: validate class D facility length before reading it
>
> The over-read lands one byte past the packet data but inside the skb
> data allocation, where KASAN does not report it, and the value read
> then controls how many further bytes the parser believes remain.
[Severity: Medium]
[Finding: main-1-0]
[Sources: opus-5]
Does the value read actually reach the parser's length arithmetic? In the
only case where p[1] is outside the validated range, len == 1, the
pre-existing check already rejected the frame:
net/x25/x25_facilities.c:x25_parse_facilities() {
unsigned int len;
...
len = skb->data[0];
if (!pskb_may_pull(skb, 1 + len))
return -1;
p = skb->data + 1;
...
case X25_FAC_CLASS_D:
if (len < p[1] + 2)
return -1;
}
len is unsigned int, p[1] is unsigned char so p[1] + 2 is an int in
[2, 257], and the comparison is done after conversion to unsigned, so
1 < p[1] + 2 holds for every possible byte value.
That means the len -= p[1] + 2; / p += p[1] + 2; statements and the
CALLING_AE/CALLED_AE memcpy() paths were unreachable for len == 1 both
before and after this change, and the new len < 2 clause returns the same
-1 for the same input.
Would it be more accurate to describe this as removing a benign one-byte
read rather than as a parser length bug, given the commit message itself
notes the read stays inside the skb head allocation? Does the
Fixes: 1da177e4c3f4 tag still make sense if there is no behavioural
change for stable trees to pick up?
> diff --git a/net/x25/x25_facilities.c b/net/x25/x25_facilities.c
> index 6dadb217e101..bfb269538984 100644
> --- a/net/x25/x25_facilities.c
> +++ b/net/x25/x25_facilities.c
> @@ -147,7 +147,7 @@ int x25_parse_facilities(struct sk_buff *skb, struct x25_facilities *facilities,
> len -= 4;
> break;
> case X25_FAC_CLASS_D:
> - if (len < p[1] + 2)
> + if (len < 2 || len < p[1] + 2)
> return -1;
> switch (*p) {
> case X25_FAC_CALLING_AE:
[Severity: Medium]
[Finding: main-2-1]
[Sources: opus-5]
This isn't a bug introduced by this patch, but while the class D branch is
being touched: should the facility length byte and the declared semi-octet
count also be checked against each other?
The CALLING_AE and CALLED_AE cases in x25_parse_facilities() validate the
two independently:
case X25_FAC_CALLED_AE:
if (p[1] > X25_MAX_DTE_FACIL_LEN || p[1] <= 1)
return -1;
if (p[2] > X25_MAX_AE_LEN)
return -1;
dte_facs->called_len = p[2];
memcpy(dte_facs->called_ae, &p[3], p[1] - 1);
There is no test that p[1] - 1 >= (p[2] + 1) / 2, while the emitter
x25_create_facilities() ties them together:
if (dte_facs->calling_len && (facil_mask & X25_MASK_CALLING_AE)) {
unsigned int bytecount = (dte_facs->calling_len + 1) >> 1;
*p++ = X25_FAC_CALLING_AE;
*p++ = 1 + bytecount;
*p++ = dte_facs->calling_len;
memcpy(p, dte_facs->calling_ae, bytecount);
So a peer sending p[1] = 2 with p[2] = 40 makes the kernel store
called_len = 40 while copying only one byte into called_ae.
Two follow-on effects: userspace reading SIOCX25GDTEFACILITIES in
af_x25.c gets a semi-octet count for 39 semi-octets the peer never sent
(the buffers are memset at the top of x25_parse_facilities(), so this is
fabricated zero data rather than a memory disclosure), and on the accept
path x25_rx_call_request() clears only the calling side:
net/x25/af_x25.c:x25_rx_call_request() {
/* ensure no calling address extension on accept */
makex25->vc_facil_mask &= ~X25_MASK_CALLING_AE;
}
so the peer-supplied called_len survives into makex25->dte_facilities and
is fed back to x25_create_facilities() when x25_write_internal() builds
X25_CALL_ACCEPTED, emitting up to 20 mostly-zero AE bytes.
There is no memory-safety consequence here: the memcpy() length is bounded
by X25_MAX_DTE_FACIL_LEN - 1 = 20, which matches sizeof(called_ae), and
the worst-case emitted facilities block stays inside X25_MAX_FAC_LEN.
Would it make sense to reject encodings the emitter can never produce, in
the same spirit as the header-length check being added here?
--
pw-bot: changes-requested
next prev parent reply other threads:[~2026-08-21 9:26 UTC|newest]
Thread overview: 5+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-08-18 16:21 [PATCH] pptp: reject payloads shorter than the PPP protocol field Chuyf26
2026-08-21 9:07 ` Simon Horman
2026-08-21 9:42 ` Chuyf26
2026-08-21 9:26 ` Simon Horman [this message]
2026-08-21 9:42 ` Chuyf26
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260821092618.GF265046@horms.kernel.org \
--to=horms@kernel.org \
--cc=Chuyf26@linux.alibaba.com \
--cc=netdev@vger.kernel.org \
--cc=xeb@mail.ru \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox