Skip to content
UNDER THE HOOD

Library calls

GuardLibrary.cpp guards a direct call of a function of the library table (LibrarySpec.txt) that the unit does not define, before the call (RFC 0035 §2.5 and amendment 7, RFC 0036 §6):

  • Each pointer argument the row reads or writes is guarded over the bytes its term gives (bytes(t), count(t) times the element size, one element without a term), evaluated on the call’s arguments; a strlen term is evaluated by the runtime inside the string’s addressable bytes.
  • A str argument is guarded as a string: __weavec_rt_strlen(s, max, site) scans it and fails with unterminated-string when the terminator is not addressable. A printf- or scanf-family call with a constant format guards each %s argument as a string and each %n argument as an int.
  • Fortified forms (chk(…), RFC 0036 §6). A call of a fortified alias (__strcpy_chk(d, s, 8)) whose object-size argument is not (size_t)-1 checks that it writes no more than that size: inline, or by passing the size to its wrapper as limit. A write past the size within addressable bytes reports intra-object-overflow.
  • A row’s wrapper(NAME) clause replaces the call with __weavec_rt_NAME, with the call’s arguments, a limit when the row has a fortified alias, and the site (first when the call is variadic). The wrappers (runtime/weavec_libc.c) of strcpy, stpcpy, strcat, sprintf, vsprintf, snprintf, vsnprintf and gets compute what the call writes before it writes; the printf-family ones check each %s and %n argument, through a va_list too; the scanf family checks what each conversion wrote; those of memchr, strchr, strcmp, strncmp, strcasecmp and strncasecmp check only the bytes the call read up to where it stopped; the number parsers (strtol to strtold, strtoimax, strtoumax, atoi, atol, atoll, atof) check after the call the bytes the C library read (up to the end pointer and the character that stopped it); mmap and munmap clear the shadow of what they map. A wrapper is named after its row’s function (the parser checks it). The calls that write through no pointer are redirected late, by GuardExpand, so that the optimiser can still fold strcmp of constants; GuardInsert guards only the first byte of their string arguments. Such a call that reads exactly a constant of at most 113 bytes keeps inline guards instead.
  • A row whose result is a value(t) computed only from what it reads (strlen, strnlen) is replaced by the string guard that computes t.
  • A wide row scales its count terms by the module’s wchar_size; its strings are not scanned (a known gap: a wide string argument is not guarded).
  • A null the row accepts (null-ok) passes. The arguments a call releases or reallocates are not guarded; the allocator validates them.
  • A copy’s operands must not overlap: llvm.memcpy (except an aggregate assignment, which carries !tbaa.struct) and a row with a disjoint clause get a __weavec.disjoint marker, and an overlap fails as overlapping-copy.

A function outside the table is not checked at the call; its own accesses are guarded if weavec-cc built it (assumption A2).