All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH net 0/2] ptp: fix scaled_ppm_to_ppb() overflow bypassing the max_adj check
@ 2026-07-12  4:09 Deep Shah
  2026-07-12  4:09 ` [PATCH net 1/2] ptp: reject frequency adjustments that overflow scaled_ppm_to_ppb() Deep Shah
                   ` (2 more replies)
  0 siblings, 3 replies; 4+ messages in thread
From: Deep Shah @ 2026-07-12  4:09 UTC (permalink / raw)
  To: netdev, Richard Cochran, David S . Miller, Jakub Kicinski,
	Paolo Abeni, Eric Dumazet, Andrew Lunn
  Cc: linux-kernel, Shuah Khan, linux-kselftest, Deep Shah

ptp_clock_adjtime() validates an ADJ_FREQUENCY request by converting
tx->freq to ppb and comparing it against ops->max_adj. On 64-bit systems
that conversion can overflow s64 and wrap the result back into range, so
a crafted tx->freq bypasses the check and reaches ->adjfine() unclamped.

Patch 1 rejects the overflow in ptp_clock_adjtime().
Patch 2 adds a regression test that crafts struct timex.freq directly
(testptp's int-ppb path cannot express the value) and confirms it is
rejected with -ERANGE.

Deep Shah (2):
  ptp: reject frequency adjustments that overflow scaled_ppm_to_ppb()
  selftests: ptp: add a regression test for the frequency adjustment
    overflow

 drivers/ptp/ptp_clock.c                       | 14 +++-
 tools/testing/selftests/ptp/Makefile          |  2 +-
 .../testing/selftests/ptp/ptp_freq_overflow.c | 74 +++++++++++++++++++
 3 files changed, 88 insertions(+), 2 deletions(-)
 create mode 100644 tools/testing/selftests/ptp/ptp_freq_overflow.c


base-commit: 3f1f755366687d051174739fb99f7d560202f60b
-- 
2.43.0


^ permalink raw reply	[flat|nested] 4+ messages in thread

* [PATCH net 1/2] ptp: reject frequency adjustments that overflow scaled_ppm_to_ppb()
  2026-07-12  4:09 [PATCH net 0/2] ptp: fix scaled_ppm_to_ppb() overflow bypassing the max_adj check Deep Shah
@ 2026-07-12  4:09 ` Deep Shah
  2026-07-12  4:09 ` [PATCH net 2/2] selftests: ptp: add a regression test for the frequency adjustment overflow Deep Shah
  2026-07-12 12:16 ` [PATCH net 0/2] ptp: fix scaled_ppm_to_ppb() overflow bypassing the max_adj check Vadim Fedorenko
  2 siblings, 0 replies; 4+ messages in thread
From: Deep Shah @ 2026-07-12  4:09 UTC (permalink / raw)
  To: netdev, Richard Cochran, David S . Miller, Jakub Kicinski,
	Paolo Abeni, Eric Dumazet, Andrew Lunn
  Cc: linux-kernel, Shuah Khan, linux-kselftest, Deep Shah

ptp_clock_adjtime() converts tx->freq to ppb with scaled_ppm_to_ppb()
and rejects the request if it exceeds ops->max_adj.  On 64-bit systems
that conversion computes (1 + ppm) * 125 in s64, which can overflow for
a large tx->freq and wrap the result back into [-max_adj, max_adj].  The
check then passes and the original out-of-range value is handed to
->adjfine().

For example tx->freq = 147573952589676412 makes (1 + ppm) * 125 equal
2^64 + 9, which wraps to ppb == 0 and is accepted.

Reject the request with -ERANGE if either the addition or the
multiplication overflows.  This hardens the max_adj sanity check and is
not a security fix.  It follows up commit 475b92f93216 ("ptp: improve
max_adj check against unreasonable values"), which fixed the analogous
s32 narrowing but not this overflow.

Fixes: d39a743511cd ("ptp: validate the requested frequency adjustment.")
Signed-off-by: Deep Shah <deepshah146@gmail.com>
---
 drivers/ptp/ptp_clock.c | 14 +++++++++++++-
 1 file changed, 13 insertions(+), 1 deletion(-)

diff --git a/drivers/ptp/ptp_clock.c b/drivers/ptp/ptp_clock.c
index d6f54ccaf93b..f83aa44b0a74 100644
--- a/drivers/ptp/ptp_clock.c
+++ b/drivers/ptp/ptp_clock.c
@@ -9,6 +9,7 @@
 #include <linux/init.h>
 #include <linux/kernel.h>
 #include <linux/module.h>
+#include <linux/overflow.h>
 #include <linux/posix-clock.h>
 #include <linux/pps_kernel.h>
 #include <linux/property.h>
@@ -159,7 +160,18 @@ static int ptp_clock_adjtime(struct posix_clock *pc, struct __kernel_timex *tx)
 		delta = ktime_to_ns(kt);
 		err = ops->adjtime(ops, delta);
 	} else if (tx->modes & ADJ_FREQUENCY) {
-		long ppb = scaled_ppm_to_ppb(tx->freq);
+		long ppb;
+		s64 tmp;
+
+		/*
+		 * scaled_ppm_to_ppb() multiplies (1 + freq) by 125 in s64;
+		 * reject a ->freq large enough to overflow that, which could
+		 * otherwise wrap the result back into the max_adj range.
+		 */
+		if (check_add_overflow(tx->freq, 1, &tmp) ||
+		    check_mul_overflow(tmp, 125, &tmp))
+			return -ERANGE;
+		ppb = scaled_ppm_to_ppb(tx->freq);
 		if (ppb > ops->max_adj || ppb < -ops->max_adj)
 			return -ERANGE;
 		err = ops->adjfine(ops, tx->freq);
-- 
2.43.0


^ permalink raw reply related	[flat|nested] 4+ messages in thread

* [PATCH net 2/2] selftests: ptp: add a regression test for the frequency adjustment overflow
  2026-07-12  4:09 [PATCH net 0/2] ptp: fix scaled_ppm_to_ppb() overflow bypassing the max_adj check Deep Shah
  2026-07-12  4:09 ` [PATCH net 1/2] ptp: reject frequency adjustments that overflow scaled_ppm_to_ppb() Deep Shah
@ 2026-07-12  4:09 ` Deep Shah
  2026-07-12 12:16 ` [PATCH net 0/2] ptp: fix scaled_ppm_to_ppb() overflow bypassing the max_adj check Vadim Fedorenko
  2 siblings, 0 replies; 4+ messages in thread
From: Deep Shah @ 2026-07-12  4:09 UTC (permalink / raw)
  To: netdev, Richard Cochran, David S . Miller, Jakub Kicinski,
	Paolo Abeni, Eric Dumazet, Andrew Lunn
  Cc: linux-kernel, Shuah Khan, linux-kselftest, Deep Shah

testptp's -f option stores the requested adjustment as an int ppb and
converts it to scaled ppm, so it cannot express the 64-bit scaled-ppm
values needed to overflow scaled_ppm_to_ppb() and bypass the max_adj
check enforced by ptp_clock_adjtime().

Add a small test that crafts struct timex.freq directly and verifies that
an overflowing frequency adjustment is rejected with -ERANGE.  The test
skips when no frequency-adjustable PTP device is available.

Signed-off-by: Deep Shah <deepshah146@gmail.com>
---
 tools/testing/selftests/ptp/Makefile          |  2 +-
 .../testing/selftests/ptp/ptp_freq_overflow.c | 74 +++++++++++++++++++
 2 files changed, 75 insertions(+), 1 deletion(-)
 create mode 100644 tools/testing/selftests/ptp/ptp_freq_overflow.c

diff --git a/tools/testing/selftests/ptp/Makefile b/tools/testing/selftests/ptp/Makefile
index 8f57f88ecadd..dd7376cc9bf5 100644
--- a/tools/testing/selftests/ptp/Makefile
+++ b/tools/testing/selftests/ptp/Makefile
@@ -1,6 +1,6 @@
 # SPDX-License-Identifier: GPL-2.0
 CFLAGS += $(KHDR_INCLUDES)
-TEST_GEN_PROGS := testptp
+TEST_GEN_PROGS := testptp ptp_freq_overflow
 LDLIBS += -lrt
 TEST_PROGS = phc.sh
 
diff --git a/tools/testing/selftests/ptp/ptp_freq_overflow.c b/tools/testing/selftests/ptp/ptp_freq_overflow.c
new file mode 100644
index 000000000000..342b22689f67
--- /dev/null
+++ b/tools/testing/selftests/ptp/ptp_freq_overflow.c
@@ -0,0 +1,74 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Regression test for the scaled_ppm_to_ppb() integer overflow that allowed
+ * a crafted clock_adjtime(ADJ_FREQUENCY) to bypass the PTP max_adj check.
+ *
+ * testptp's -f option stores the adjustment as an int ppb and cannot express
+ * the 64-bit scaled-ppm values needed to overflow the conversion, so this
+ * test crafts struct timex.freq directly.
+ */
+#define _GNU_SOURCE
+#define __SANE_USERSPACE_TYPES__
+#include <errno.h>
+#include <fcntl.h>
+#include <string.h>
+#include <sys/ioctl.h>
+#include <sys/timex.h>
+#include <time.h>
+#include <unistd.h>
+#include <linux/ptp_clock.h>
+#include "../kselftest.h"
+
+#define FD_TO_CLOCKID(fd)	((~(clockid_t)(fd) << 3) | 3)
+
+/* clock_adjtime is not available in GLIBC < 2.14 */
+#if !__GLIBC_PREREQ(2, 14)
+#include <sys/syscall.h>
+static int clock_adjtime(clockid_t id, struct timex *tx)
+{
+	return syscall(__NR_clock_adjtime, id, tx);
+}
+#endif
+
+int main(int argc, char *argv[])
+{
+	const char *device = argc > 1 ? argv[1] : "/dev/ptp0";
+	struct ptp_clock_caps caps;
+	struct timex tx = { 0 };
+	clockid_t clkid;
+	int fd, ret;
+
+	ksft_print_header();
+	ksft_set_plan(1);
+
+	if (sizeof(tx.freq) < 8)
+		ksft_exit_skip("the overflow only affects 64-bit kernels\n");
+
+	fd = open(device, O_RDWR);
+	if (fd < 0)
+		ksft_exit_skip("cannot open %s: %s\n", device, strerror(errno));
+
+	clkid = FD_TO_CLOCKID(fd);
+
+	if (ioctl(fd, PTP_CLOCK_GETCAPS, &caps))
+		ksft_exit_skip("PTP_CLOCK_GETCAPS on %s: %s\n", device, strerror(errno));
+	if (!caps.max_adj)
+		ksft_exit_skip("%s does not support frequency adjustment\n", device);
+
+	/*
+	 * (1 + 147573952589676412) * 125 == 2^64 + 9, which overflows s64 in
+	 * scaled_ppm_to_ppb() and wraps the result to a ppb of 0.  A kernel
+	 * that does not detect the overflow lets this absurd frequency past
+	 * the max_adj check; a fixed kernel rejects it with -ERANGE.
+	 */
+	tx.modes = ADJ_FREQUENCY;
+	tx.freq = 147573952589676412LL;
+
+	ret = clock_adjtime(clkid, &tx);
+	ksft_test_result(ret < 0 && errno == ERANGE,
+			 "overflowing frequency adjustment is rejected (ret=%d errno=%d)\n",
+			 ret, ret < 0 ? errno : 0);
+
+	close(fd);
+	ksft_finished();
+}
-- 
2.43.0


^ permalink raw reply related	[flat|nested] 4+ messages in thread

* Re: [PATCH net 0/2] ptp: fix scaled_ppm_to_ppb() overflow bypassing the max_adj check
  2026-07-12  4:09 [PATCH net 0/2] ptp: fix scaled_ppm_to_ppb() overflow bypassing the max_adj check Deep Shah
  2026-07-12  4:09 ` [PATCH net 1/2] ptp: reject frequency adjustments that overflow scaled_ppm_to_ppb() Deep Shah
  2026-07-12  4:09 ` [PATCH net 2/2] selftests: ptp: add a regression test for the frequency adjustment overflow Deep Shah
@ 2026-07-12 12:16 ` Vadim Fedorenko
  2 siblings, 0 replies; 4+ messages in thread
From: Vadim Fedorenko @ 2026-07-12 12:16 UTC (permalink / raw)
  To: Deep Shah, netdev, Richard Cochran, David S . Miller,
	Jakub Kicinski, Paolo Abeni, Eric Dumazet, Andrew Lunn
  Cc: linux-kernel, Shuah Khan, linux-kselftest

On 12/07/2026 05:09, Deep Shah wrote:
> ptp_clock_adjtime() validates an ADJ_FREQUENCY request by converting
> tx->freq to ppb and comparing it against ops->max_adj. On 64-bit systems
> that conversion can overflow s64 and wrap the result back into range, so
> a crafted tx->freq bypasses the check and reaches ->adjfine() unclamped.
> 
> Patch 1 rejects the overflow in ptp_clock_adjtime().
> Patch 2 adds a regression test that crafts struct timex.freq directly
> (testptp's int-ppb path cannot express the value) and confirms it is
> rejected with -ERANGE.
> 
> Deep Shah (2):
>    ptp: reject frequency adjustments that overflow scaled_ppm_to_ppb()
>    selftests: ptp: add a regression test for the frequency adjustment
>      overflow
> 
>   drivers/ptp/ptp_clock.c                       | 14 +++-
>   tools/testing/selftests/ptp/Makefile          |  2 +-
>   .../testing/selftests/ptp/ptp_freq_overflow.c | 74 +++++++++++++++++++
>   3 files changed, 88 insertions(+), 2 deletions(-)
>   create mode 100644 tools/testing/selftests/ptp/ptp_freq_overflow.c
> 
> 
> base-commit: 3f1f755366687d051174739fb99f7d560202f60b

For the series:
Reviewed-by: Vadim Fedorenko <vadim.fedorenko@linux.dev>

^ permalink raw reply	[flat|nested] 4+ messages in thread

end of thread, other threads:[~2026-07-12 12:17 UTC | newest]

Thread overview: 4+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-07-12  4:09 [PATCH net 0/2] ptp: fix scaled_ppm_to_ppb() overflow bypassing the max_adj check Deep Shah
2026-07-12  4:09 ` [PATCH net 1/2] ptp: reject frequency adjustments that overflow scaled_ppm_to_ppb() Deep Shah
2026-07-12  4:09 ` [PATCH net 2/2] selftests: ptp: add a regression test for the frequency adjustment overflow Deep Shah
2026-07-12 12:16 ` [PATCH net 0/2] ptp: fix scaled_ppm_to_ppb() overflow bypassing the max_adj check Vadim Fedorenko

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.