Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
V
varnish-cache
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Commits
Open sidebar
varnishcache
varnish-cache
Commits
4f656638
Commit
4f656638
authored
Nov 29, 2022
by
Poul-Henning Kamp
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Chapter 4
parent
953960e6
Changes
3
Hide whitespace changes
Inline
Side-by-side
Showing
3 changed files
with
181 additions
and
1 deletion
+181
-1
cheri3.rst
doc/sphinx/phk/cheri3.rst
+1
-1
cheri4.rst
doc/sphinx/phk/cheri4.rst
+179
-0
index.rst
doc/sphinx/phk/index.rst
+1
-0
No files found.
doc/sphinx/phk/cheri3.rst
View file @
4f656638
...
...
@@ -43,7 +43,7 @@ ever be able to see the pointers, and those three test cases pass::
only reason it exists is precisely to to resurrect those pointers later.)
The final and really nasty test-case
====================================
------------------------------------
As previously mentioned, when you have 100K threads, you have to be
stingy with memory, in particular with the thread stacks.
...
...
doc/sphinx/phk/cheri4.rst
0 → 100644
View file @
4f656638
.. _phk_cheri_4:
How Varnish met CHERI 4/N
=========================
So what can CHERI do ?
----------------------
CHERI can restrict pointers, but it does not restrict the memory
they point to::
#include <cheriintrin.h>
#include <stdio.h>
#include <string.h>
int
main()
{
char buf[20];
char *ptr1 = cheri_perms_and(buf, CHERI_PERM_LOAD);
char *ptr2 = buf;
strcpy(buf, "Hello World\n");
ptr1[5] = '_'; // Will core dump
ptr2[5] = '_'; // Works fine.
puts(buf);
return (0);
}
I suspect most programmers will find this counter-intuitive, because
normally it is the memory itself which write-protected in which
case no pointers can ever write to it.
This is where the word "capability" comes from: The pointer is what
gives you the "capability" to access the memory, and therefore they
can be restricted separately from the memory they provide access to.
If you could just create your own "capabilities" out of integers,
that would be no big improvement, but you can not: Under CHERI you
can only make a new capability from another capability, and the new
one can never be more potent than the one it is derived from.
In addition to 'read' and 'write' permissions, capabilities contain the
start and the length of the piece of memory they allow access to.
Under CHERI the printf(3) pattern "%#p" tells the full story::
#include <cheriintrin.h>
#include <stdio.h>
#include <string.h>
int
main()
{
char buf[20];
char *ptr1 = cheri_perms_and(buf, CHERI_PERM_LOAD);
char *ptr2 = buf;
char *ptr3;
char *ptr4;
strcpy(buf, "Hello World\n");
//ptr1[5] = '_'; // Will core dump
ptr2[5] = '_'; // Works fine.
puts(buf);
printf("buf:\t%#p\n", buf);
printf("ptr1:\t%#p\n", ptr1);
printf("ptr2:\t%#p\n", ptr2);
ptr3 = ptr2 + 1;
printf("ptr3:\t%#p\n", ptr3);
ptr4 = cheri_bounds_set(ptr3, 4);
printf("ptr4:\t%#p\n", ptr4);
return (0);
}
And we get::
buf: 0xfffffff7ff68 [rwRW,0xfffffff7ff68-0xfffffff7ff7c]
ptr1: 0xfffffff7ff68 [r,0xfffffff7ff68-0xfffffff7ff7c]
ptr2: 0xfffffff7ff68 [rwRW,0xfffffff7ff68-0xfffffff7ff7c]
ptr3: 0xfffffff7ff69 [rwRW,0xfffffff7ff68-0xfffffff7ff7c]
ptr4: 0xfffffff7ff69 [rwRW,0xfffffff7ff69-0xfffffff7ff6d]
(Ignore the upper-case 'RW' for now, I'll get back to those later.)
Because of the odd things people do with pointers in C, ``ptr3``
keeps the same range as ``ptr2`` and that's a good excuse to
address the question I imagine my readers have at this point:
What about ``const`` ?
----------------------
The C language is a mess.
Bjarne Stroustrup introduced ``const`` in "C-with-classes",
which later became C++, and in C++ it does the right thing.
The morons on the ISO-C committee did a half-assed import
in C89, and therefore it does the wrong thing::
char *strchr(const char *s, int c);
You pass a read-only pointer in, and you get a read-write
pointer into that string back ?!
There were at least three different ways they could have done right:
* Add a ``cstrchr`` function, having ``const`` on both sides.
* Enable prototypes to explain this, with some gruesome hack::
(const) char *strchr((const) char *s, int c);
* Allow multiple prototypes for the same function, varying only in const-ness::
char *strchr(char *s, int c);
const char *strchr(const char *s, int c);
But instead they just ignored that issue, and several others like it.
The result is that we have developed the concept of "const-poisoning",
to describe the fact that if you use "const" to any extent in your
C sources, you almost always end up needing a macro like::
#define TRUST_ME(ptr) ((void*)(uintptr_t)(ptr))
To remove const-ness where it cannot go.
(If you think that is ISO-C's opus magnum, ask yourself why we still
cannot specify struct packing and endianess explicitly ? It's hardly
like anybody ever have to apart data-structures explicitly specified in
hardware or protocol documents, is it ?)
Read/Write markup with CHERI
----------------------------
Because ``const`` is such a mess in C, the CHERI compiler does not
automatically remove the write-capability from ``const`` arguments
to functions, something I suspect (but have not checked) that they
can do in C++.
Instead we will have to do it ourselves, so I have added two macros to
our ``<vdef.h>`` file::
#define RO(x) cheri_perms_and((x), CHERI_PERM_LOAD)
#define ROP(x) cheri_perms_and((x), CHERI_PERM_LOAD|CHERI_PERM_LOAD_CAP)
Passing a pointer through the ``RO()`` macro makes it read-only, so we
can do stuff like::
@@ -285,7 +286,7 @@ VRT_GetHdr(VRT_CTX, VCL_HEADER hs)
[…]
- return (p);
+ return (RO(p));
}
To explicitly give ``const`` some bite.
The difference between ``RO`` and ``ROP`` is where the upper- and
lower-case "rw" comes into the picture: Capabilities have two
levels of read/write protection:
* Can you read or write normal data with this pointer (``CHERI_PERM_LOAD``)
* Can you read or write pointers with this pointer (``CHERI_PERM_LOAD_CAP``)
Rule of thumb: Pure data: Use only the first, structs with pointers in them,
use both.
One can also make write-only pointers with CHERI, but there are
only few places where they can be gainfully employed, outside strict
security in handling of (cryptographic) secrets.
Right now I'm plunking ``RO()`` and ``ROP()`` into the varnish code,
and one by one relearning what atrocity the 37 uses of ``TRUST_ME()``
hide.
Still no bugs found.
*/phk*
doc/sphinx/phk/index.rst
View file @
4f656638
...
...
@@ -13,6 +13,7 @@ You may or may not want to know what Poul-Henning thinks.
.. toctree::
:maxdepth: 1
cheri4.rst
cheri3.rst
cheri2.rst
cheri1.rst
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment