Hi,
Il 19/12/24 16:47, Diego Nieto Cid ha scritto:
Hello,
After playing a bit with the setrlimit calls in Linux to see how the
resource RLIMIT_AS actually works, it seems to be limiting the amount
of virtual memory available to the process (and not the available
virtual address range).
I see that some limits (e.g. RLIMIT_DATA) are managed in glibc instead
of gnumach, maybe this could be a simpler way to add some minimal
support? I guess that one might overcome these limits by using directly
the mach rpc or hijacking glibc, but it could be enough for some use cases.
So, I'm attempting to implement this limit in gnumach at the points
memory is allocated. The patch below contains the first few steps
I took in this attempt:
1. Add a `hard_limit` field, of type size_t, to the vm_map struct.
I started with hard limit beacause I still need to research how
soft limits work. So, for now, it's a plain rejection with ENOMEM.
2. At vm_map_setup, initialize the `hard_limit` field with the
appropriate value which should be RLIM_INFINITY. In this first
attempt, I hardcoded 8GB for testing.
I tried a lower value, like 2GB, but some process is mapping
4GB at once during boot and it just hangs when the allocation
fails.
Which process is that?
3. Finally, enforce the limit in `vm_allocate`, `vm_map` and
`vm_allocate_contiguous` by checking that current map size
(`size` field) plus the requested size (`size` param) is less
than the current map's `hard_limit` field.
I thought of adding an RPC call that sets the `hard_limit` field
which, I guess, should be located among the other task related RPCs.
One big point to address is how to enforce the ability to change this
limit, e.g. an unprivileged task shouldn't be able to increase its own
memory limit. You could reuse the host privileged port, but maybe it
could make sense to have a dedicated one for resource limits?
Also, I wanted to ask whether I covered all the allocation points
or there is somewhere else where limits shall be enforced. For instance,
something I still have to look at is the out-of-line data sent through
a mach_msg call.
One additional point would be at least in task_create(), I guess the new
task would have the same restriction as the one creating it.
To experiment I'd suggest creating a test program as the ones in the
tests/ folder in gnumach source, so you can try every single case and
easily debug it. Currently they can only be run on GNU/Linux as they
require qemu.
----
Index: gnumach-1.8+git20240714/vm/vm_map.c
It would be better to create the patches from the git repository instead
of the debian package, and then use git-format-patch and git-send-mail.
===================================================================
--- gnumach-1.8+git20240714.orig/vm/vm_map.c
+++ gnumach-1.8+git20240714/vm/vm_map.c
@@ -198,6 +198,9 @@ void vm_map_setup(
map->first_free = vm_map_to_entry(map);
map->hint = vm_map_to_entry(map);
map->name = NULL;
+ /* TODO hardcoded limit for testing purposes, rather use RLIM_INFINITY
*/
+ /* TODO add RPC to update this limit */
+ map->hard_limit = 8l * 1024l * 1024l * 1024l;
vm_map_lock_init(map);
simple_lock_init(&map->ref_lock);
simple_lock_init(&map->hint_lock);
Index: gnumach-1.8+git20240714/vm/vm_map.h
===================================================================
--- gnumach-1.8+git20240714.orig/vm/vm_map.h
+++ gnumach-1.8+git20240714/vm/vm_map.h
@@ -198,6 +198,10 @@ struct vm_map {
unsigned int timestamp; /* Version number */
const char *name; /* Associated name */
+
+ /* TODO only hard limits are enforced */
+ /* TODO does getting rlim_t here make sense? */
+ vm_size_t hard_limit; /* hard limit as set by
RLIMIT_AS */
};
#define vm_map_to_entry(map) ((struct vm_map_entry *) &(map)->hdr.links)
Index: gnumach-1.8+git20240714/vm/vm_user.c
===================================================================
--- gnumach-1.8+git20240714.orig/vm/vm_user.c
+++ gnumach-1.8+git20240714/vm/vm_user.c
@@ -81,6 +81,12 @@ kern_return_t vm_allocate(
*addr = trunc_page(*addr);
size = round_page(size);
+ if (map->size + size > map->hard_limit)
+ {
+ printf("map size: %lu, requested size: %lu, hard limit: %lu\n",
map->size, size, map->hard_limit);
+ return(KERN_NO_SPACE);
+ }
+
Beware of unsigned integer wrap, e.g. if size is very big (see [0] for
example).
Luca
[0]
https://wiki.sei.cmu.edu/confluence/display/c/INT30-C.+Ensure+that+unsigned+integer+operations+do+not+wrap