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


Reply via email to