Page MenuHomeFreeBSD

ada: Fix intra-object buffer overread of identify strings
ClosedPublic

Authored by jrtc27 on Oct 19 2021, 7:39 PM.
Tags
None
Referenced Files
Unknown Object (File)
Sun, Dec 15, 10:02 AM
Unknown Object (File)
Tue, Dec 10, 3:25 AM
Unknown Object (File)
Tue, Dec 10, 1:04 AM
Unknown Object (File)
Tue, Dec 3, 4:40 PM
Unknown Object (File)
Oct 25 2024, 4:28 AM
Unknown Object (File)
Oct 25 2024, 4:27 AM
Unknown Object (File)
Oct 25 2024, 4:27 AM
Unknown Object (File)
Oct 25 2024, 4:17 AM
Subscribers

Details

Summary

In the ATA/ATAPI spec these are space-padded fixed-length strings with
no NUL-terminator (and byte swapped). When performing the identify we
call ata_param_fixup to swap the bytes back to be in order, strip any
leading/trailing spaces and coalesce consecutive spaces, padding with
NULs. However, if the input has no padding spaces, the fixed-up strings
are still not NUL-terminated. This causes two issues. The first is that
strlcpy will truncate the string by replacing the final byte with a NUL.
The second is that strlcpy will keep reading src until it finds a NUL in
order to calculate the return value, which is defined as the length of
src (so that callers can then compare it with the dsize input to see if
the input string was truncated), thereby reading past the end of the
buffer and into whatever adjacent fields are in the structure. In
practice there's a NUL byte somewhere in the structure, but on CHERI
with subobject bounds enabled in the compiler this overread will be
detected and trap as a bounds violation.

Note this matches ata_xpt's aprobedone, which does a bcopy to a
malloc'ed buffer and manually NUL-terminates it for the CAM path's
device's serial_num.

Found by: CHERI

Diff Detail

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

Event Timeline

This revision is now accepted and ready to land.Oct 19 2021, 8:25 PM
scottl added a subscriber: scottl.

A minor follow-up to this would be to use macros for the serial and model field lengths, and update ata.h to use the same, so that there's no reliance on sizeof.

A minor follow-up to this would be to use macros for the serial and model field lengths, and update ata.h to use the same, so that there's no reliance on sizeof.

Not a huge fan of that, there are often magic +1's floating around with string length macros (i.e. is the buffer a char buf[BUFLEN] or char buf[BUFLEN + 1]? both exist throughout the codebase). This way everything is guaranteed to be consistent, no matter how the buffer size is defined.

The following script

// xxx
@@
struct ccb_getdev *cgd;
expression E1, E2;
@@
- strlcpy(E1,cgd->ident_data.model,E2)
+ THIS_IS_BOGUS(E1,cgd->ident_data.model,E2)

@@
struct ata_model m;
expression E1, E2;
@@
- strlcpy(E1,m.model,E2)
+ THIS_IS_BOGUS(E1,m.model,E2)

@@
struct ata_model *m;
expression E1, E2;
@@
- strlcpy(E1,m->model,E2)
+ THIS_IS_BOGUS(E1,m->model,E2)

@@
struct ccb_getdev *cgd;
char *s;
expression E1, E2;
@@
- strlcpy(E1,cgd->ident_data.serial,E2)
+ THIS_IS_BOGUS(E1,cgd->ident_data.serial,E2)

@@
struct ata_model m;
char *s;
expression E1, E2;
@@
- strlcpy(E1,m.serial,E2)
+ THIS_IS_BOGUS(E1,m.serial,E2)

@@
struct ata_model *m;
char *s;
expression E1, E2;
@@
- strlcpy(E1,m->serial,E2)
+ THIS_IS_BOGUS(E1,m->serial,E2)

Found no further instances of this in the tree