Original message's format is broken due to e-mail client issues, re-sending
with the correct format.

---------Original Message---------

Dear OpenBSD Team,

We have found several privsep interface bugs in `ntpd`, where the
unprivileged `ntp` compartment can trigger undefined behavior inside
the privileged compartment. We think that these bugs have *no security
impact* but are still worth fixing, thus sending this report to
bugs@.

The privileged compartment accepts multiple messages from the
unprivileged `ntp` compartment, where the payload is a `double`
arithmetic value. We identified several cases where the privileged
parent performs a lossy conversion from `double` to `long` to extract
the integer part of the `double`. This conversion is well-defined only
when the `double` value is between `LONG_MIN` and `LONG_MAX`.
However, the privileged compartment does not verify whether the
`double` is in this range before the conversion to a `long`, allowing
the unprivileged `ntp` compartment to send a value outside this range,
thus triggering undefined behavior inside the privileged compartment.

In our proof of concept, we set up the `ntp` compartment to send an
`IMSG_ADJTIME` message with a very large `double` value (2e100) as the
payload. In the privileged handler, this value is read into the
variable `d` (`ntpd.c:411`) and passed to `ntpd_adjtime`
(`ntpd.c:412`). The value is then passed to the function `d_to_tv`
(`ntpd.c:477`), where it is assigned to `tv->tv_sec` (util.c:79), a
`long`-typed variable. Since `d` exceeds `LONG_MAX`, this assignment
results in undefined behavior. In our case, the value of `tv->tv_sec`
becomes `LONG_MIN`. We also found a similar bug in `ntpd.c:502`, where
an undefined conversion could occur when converting `relfreq`
(`double`) to `curfreq` (`uint64_t`).

We believe these bugs are not exploitable to escalate privileges, as
with most reasonable compilers they will only result in corrupting the
`double` value sent by the unprivileged compartment, which the
unprivileged compartment already controls. Nonetheless, we consider
this issue worth addressing as we think that unprivileged compartments
should not be able to trigger undefined behavior in privileged
parents.

For context, we assume that the unprivileged compartment has been
compromised and can send arbitrary messages to the privileged
compartment to trigger interface bugs that would allow it to escalate
privileges. We found this bug while looking for such interface bugs.

Regards,
Shibo, Shawn, Hugo
Systopia Team


ntpd.c
395  double                   d;
...
408  case IMSG_ADJTIME:
409          if (imsg.hdr.len != IMSG_HEADER_SIZE + sizeof(d))
410                  fatalx("invalid IMSG_ADJTIME received");
411          memcpy(&d, imsg.data, sizeof(d));
412          n = ntpd_adjtime(d);
413          imsg_compose(ibuf, IMSG_ADJTIME, 0, 0, -1,
414               &n, sizeof(n));
415          break;
...
465 ntpd_adjtime(double d)
466 {
467         struct timeval  tv, olddelta;
468         int             synced = 0;
469         static int      firstadj = 1;
470
471         d += getoffset();
472         if (d >= (double)LOG_NEGLIGIBLE_ADJTIME / 1000 ||
473             d <= -1 * (double)LOG_NEGLIGIBLE_ADJTIME / 1000)
474                 log_info("adjusting local clock by %fs", d);
475         else
476                 log_debug("adjusting local clock by %fs", d);
477         d_to_tv(d, &tv);
...
486 void
487 ntpd_adjfreq(double relfreq, int wrlog)
488 {
489         int64_t curfreq;
490         double ppmfreq;
491         int r;
492
493         if (adjfreq(NULL, &curfreq) == -1) {
494                 log_warn("adjfreq failed");
495                 return;
496         }
497
498         /*
499          * adjfreq's unit is ns/s shifted left 32; convert relfreq to
500          * that unit before adding. We log values in part per million.
501          */
502         curfreq += relfreq * 1e9 * (1LL << 32);

util.c
76 void
77 d_to_tv(double d, struct timeval *tv)
78 {
79         tv->tv_sec = d;
80         tv->tv_usec = (d - tv->tv_sec) * 1000000;
81         while (tv->tv_usec < 0) {
82                 tv->tv_usec += 1000000;
83                 tv->tv_sec -= 1;
84         }
85 }

On 2025-08-06 16:00, Shibo Ai wrote:
> Dear OpenBSD Team,
>
> We have found several privsep interface bugs in `ntpd`, where the unprivileged
> `ntp` compartment can trigger undefined behavior inside the privileged
> compartment. We think that these bugs have *no security impact* but are still
> worth fixing, thus sending this report to bugs@.

Reply via email to