svn commit: r292777 - in head: lib/libc/sys sys/kern

Bruce Evans brde at optusnet.com.au
Mon Dec 28 21:15:47 UTC 2015


On Mon, 28 Dec 2015, Garrett Cooper wrote:

>
>> On Dec 28, 2015, at 02:17, Bruce Evans <brde at optusnet.com.au> wrote:
>
> ...
>
>> It is not unreasonable to panic when such tests fail, just like for other
>> settings of unreasonable values.  Only the superuser can make them, and
>> the superuser should know better than to run them on production systems.
>
> On a development system, this is perfectly reasonable. However, on systems in production, dying on asserts or panicking when unexpected input is encountered instead of erroring out appropriately is not ideal: it causes unnecessary downtime and can confuse others (customers, lower level admins) who are not fully aware of how UNIX and FreeBSD works (and in turn generate support calls and bug reports).

This is sort of backwards.  Unexpected cases by definition have no code to
handle them.  If you are lucky then they will be detected by an assertion
or a bad pointer and cause a panic before they do further damage.  Bad
pointers works better for this since they cannot be turned off on
production systems.

Out of bounds input that is expected can be handled, but the handling can
still reasonably be to panic.  For example, add assertions at the lowest
level that values are within bounds for the BCD array.  This "handles" the
bad input that is "expected" from buggy upper layers.

Panics for expected cases are easier to avoid (or cause) than for unexpected
cases.  The super user should avoid them.  Malicious users should try to
caus them.  But when the syscall that causes them is privileged, the
malicious users can't get far.

Another easy way to trigger the panic is:
- abuse the adjkerntz sysctl to set a huge negative offset.  utc_offset()
   is normally small, so it can only produce negative times from positive
   times in the Western hemisphere for times near the Epoch.  The
   adjkerntz offset is added in utc_offset() so it can reach 68 years
   back from the current time, i.e., to 1947.

The adjkerntz sysctl, like most sysctls, is correctly implemented with
no bounds checking.  Callers are trusted to know more about the correct
bounds than the kernel.

A not so way way to trigger the panic is:
- set the time in the BIOS to before the Epoch if the BIOS supports that.
   Say 1969/01/01.
- set this time in the kernel by booting if you can do that.  On x86, this
   requires configuring with USE_RTC_CENTURY to prevent 1969 being
   interpreted as 2069.  You now have a negative time.
- find or create an ntp server that supports times before the Epoch, and
   set its time to 1969/01/01
- wait to sync with the ntp server with the fake time.  The dubious
   periodic refresh of the TODR when the time is synced with ntp is not
   even an option and the default period is 1800 seconds.  Also wait
   for the refresh.  resettodr() is then called with a negative time.

ntpd normally uses microadjustments so it is not affected by restrictions
in settime().  I don't know of any other way of trying to write back the
current time, and ntpd with a non-fake time doesn't do it because 1969
can only be in sync with a fake time.

The periodic refresh is dubious because at least on x86 the RTC reading
and setting code is sloppy and has an error of about 1 second each,
and races.  So setting the RTC more accurately than 1-2 seconds is
worse than useless (it rarely helps, and may lose to races).  It can be
kept that accurate by setting it much less often than twice per hour,
except for the initial setting after booting which is best done by
ntpdate.  Everything is best handled in userland.  adjkerntz(8) handles
more complicated things, and the adjkerntz sysctl can be used at any
time for its side effect of calling resettodr().

The badly named machdep.disable_rtc_set sysctl still exists and is still
used by adjkerntz(8) to make resettodr() do nothing.  This is normally
enabled, but adjkerntz(8) disables it transiently to avoid setting the
RTC too often.  This could be disabled more permantly to stop buggy
things like settime() and the ntpd refresh ever setting the RTC.  IIRC,
adjkerntz(8) is fairly careful with this sysctl and restores it to
its previous state after using it.

Bruce


More information about the svn-src-head mailing list