Page MenuHomeFreeBSD

Fix memory corruption bugs in BSM record parsing
ClosedPublic

Authored by csjp on Sun, Apr 19, 12:47 AM.
Tags
None
Referenced Files
F154037852: D56510.diff
Sat, Apr 25, 3:51 PM
Unknown Object (File)
Sat, Apr 25, 9:05 AM
Unknown Object (File)
Thu, Apr 23, 8:23 PM
Unknown Object (File)
Thu, Apr 23, 1:48 PM
Unknown Object (File)
Sun, Apr 19, 4:20 PM
Subscribers

Details

Summary

fetch_newgroups_tok(3): clamp group count to AUDIT_MAX_GROUPS before the loop to prevent a stack buffer overflow when a crafted record specifies more than 16 groups.

fetch_execarg_tok(3), fetch_execenv_tok(3): add a bounds check at the top of the string-walking loop to prevent an out-of-bounds read when the previous string's nul byte is the last byte of the record buffer.

fetch_sock_unix_tok(3): clamp the memchr search length to the number of bytes remaining in the buffer to prevent an out-of-bounds read on short tokens. Also clamp slen to sizeof(path) to prevent a one-byte overflow when no nul byte is found within the path data.

fetch_socket_tok: fix copy-paste error where the remote address was written into l_addr instead of r_addr.
Previously reported by: @haginara

Define AU_UNIX_PATH_MAX as 108 (the largest sun_path across all supported platforms) and use it in au_socketunix_t instead of the hardcoded 104.

Update fetch_sock_unix_tok to derive its search bound from sizeof(tok->tt.sockunix.path) so cross-platform records from Solaris and Linux with paths up to 108 bytes parse correctly without truncation.

REF: https://github.com/openbsm/openbsm/pull/87

Diff Detail

Repository
rG FreeBSD src repository
Lint
Lint Not Applicable
Unit
Tests Not Applicable

Event Timeline

csjp requested review of this revision.Sun, Apr 19, 12:47 AM
csjp edited the summary of this revision. (Show Details)
This revision is now accepted and ready to land.Mon, Apr 20, 1:54 PM
markj added inline comments.
contrib/openbsm/libbsm/bsm_io.c
3249

IMHO it is a bit strange that len is a signed int here and in the other parsing functions. Shouldn't it be a size_t?

So.. I went down a wild rabbit hole here. len is int here becauase for some
reason we used int for all the fetch_*(3) functions. Upon deeper analysis:

  1. The macros cast len to u_int32_t anyway READ_TOKEN_U_INT16 and READ_TOKEN_BYTES both do (u_int32_t)(len) internally, so a signed negative value would just wrap to a large unsigned number and the bounds check would still fail (conservatively).
  2. The negative-len guard lives in au_fetch_tok(), the public entry point, which explicitly checks if (len <= 0) return (-1) before dispatching to any fetch_* function. So by the time fetch_sock_unix_tok() is called, len is already guaranteed to be > 0.
  3. Line 3401 remaining = (size_t)(len - (int)tok->len) is the only place the sign matters, and a negative result there would wrap to a huge size_t, but it would immediately be clamped by pathmax on the next line in my fix.

That said, it's still cleaner to avoid the signed/unsigned arithmetic entirely.
I thought maybe we got it from the original Solaris/SunOS BSM libary but it
turns out that au_fetch_tok(3) wasn't even part of the original Solaris public
libbsm API (illumos classifies it as private/internal)

But I would agree we could safely switch this to size_t and simplify some code

Best,
Christian S.J. Peron

This revision was automatically updated to reflect the committed changes.