Capability to integer casts on CheriBSD

Richard Kettlewell rjk at terraraq.uk
Mon Oct 30 09:49:49 UTC 2023


On 29/10/2023 20:36, Julien ÉLIE wrote:
> Hi all,
> 
> I've just tried a build on CheriBSD, using Clang 13.
> It is said to implement memory protection and software 
> compartmentalization features enabled by CHERI-extended CPUs.
> 
> Interestingly, one of the test for checking the validity of a header 
> field body in the test suite core dumps, because of an access to a char 
> before the start of a string.
> It is easily fixed, but what strikes me most is that the error had still 
> not popped up on any other systems, nor was found by static analysers.
> 
> (I bet it would be interesting to run an INN instance for a while on 
> CheriBSD to see it does not core dumps at other places.)
> 
> 
> --- a/lib/headers.c
> +++ b/lib/headers.c
> @@ -47,6 +47,7 @@ bool
>   IsValidHeaderBody(const char *p)
>   {
>       bool emptycontentline = true;
> +    const char *start = p;
> 
>       /* Not NULL and not empty. */
>       if (p == NULL || *p == '\0')
> @@ -73,7 +74,7 @@ IsValidHeaderBody(const char *p)
>                * re-initialize emptycontentline to true. */
>               emptycontentline = true;
>               continue;
> -        } else if (p[-1] == '\r') {
> +        } else if (p > start && p[-1] == '\r') {
>               /* Case of CR not followed by LF (handled at the previous
>                * if statement). */
>               return false;
> 
> 
> 
> 
> That said, the reason I am writing is for two Clang warnings I am unsure 
> they are genuine errors to fix (and also how to fix them if they should).
> 
> 
> In lib/mmap.c, with inn__msync_page(void *p, size_t length, int flags):
> 
> mmap.c:29:33: error: cast from capability type 'void *' to 
> non-capability, non-address type 'size_t' (aka 'unsigned long') is most 
> likely an error [-Werror,-Wcapability-to-integer-cast]
>          char *start = (char *) ((size_t) p & mask);
>                                  ^~~~~~~~~~
> mmap.c:29:23: error: cast from provenance-free integer type to pointer 
> type will give pointer that can not be dereferenced 
> [-Werror,-Wcheri-capability-misuse]
>          char *start = (char *) ((size_t) p & mask);
>                        ^
> mmap.c:30:32: error: cast from capability type 'void *' to 
> non-capability, non-address type 'size_t' (aka 'unsigned long') is most 
> likely an error [-Werror,-Wcapability-to-integer-cast]
>          char *end = (char *) (((size_t) p + length + pagesize) & mask);
>                                 ^~~~~~~~~~
> mmap.c:30:21: error: cast from provenance-free integer type to pointer 
> type will give pointer that can not be dereferenced 
> [-Werror,-Wcheri-capability-misuse]
>          char *end = (char *) (((size_t) p + length + pagesize) & mask);
>                      ^

I don't know much about Cheri but AIUI pointers have extra metadata to 
enforce run-time bounds checking, so synthesizing pointers from integers 
like this does seem rather problematic - there's nowhere for the bound 
information to come from.

The idea of the code in question seems to be to convert region expressed 
by a pointer and length into the slightly wider region containing it 
consisting of whole pages.

An alternative approach that does not synthesize any pointers (but still 
relies on a pointer-to-integer conversion):

   size_t page_mask = pagesize - 1;

   // Offset of p from start of first page
   size_t start_offset = (size_t)p & page_mask;

   // Start of first page
   char *start = p - start_offset;

   // Offset of (p+length) from start of last page, or 0
   // if (p+length) is exactly on a page boundary
   size_t end_offset = (start_offset + length) & page_mask;

   // Offset _backwards_ of (p+length) from end of last page
   if(end_offset > 0)
     end_offset = page_mask - end_offset;

   // Total length of pages
   size_t total_length = start+offset + length + end_offset;

   return msync(start, total_length, flags);

Totally untested - the calculation should be out into a distinct 
function so it can be unit-tested properly.

> And a few other parts of the code, like this one:
> 
> 
> icd.c:490:16: error: cast from capability type 'char *' to 
> non-capability, non-address type 'unsigned long' is most likely an error 
> [-Werror,-Wcapability-to-integer-cast]
>          syslog(L_FATAL, "%s msync failed %s 0x%lx %d %m", LogName, 
> ICDactpath,
>                 (unsigned long) ICDactpointer, ICDactsize);
>                 ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~

That one should be %p. It would already be broken on a platform with 
32-bit long but 64-bit (or longer) pointers.

ttfn/rjk


More information about the inn-workers mailing list