google-deepmind / google-deepmind/mujoco
[Model editing] Failed allocations raise C++ exceptions through the C API
- Dominant language
- C++
- Stars
- 15.2k
- Forks
- 1.8k
- Avg merge
- 10d 16h
- Merged PRs (30d)
- 25
Description
### Intro
Hi!
I am a researcher at University of Ljubljana, I use MuJoCo for different things.
### My setup
MuJoCo 3.13.0; C API (C++ code), x84, Fedora 44.
### What's happening? What did you expect?
Model editing functions, such as [`mjs_addBody`](https://github.com/google-deepmind/mujoco/blob/10124d5d9dca411ec3c8988aa1e3b619103d71bb/src/user/user_api.cc#L689), don't provide an option to catch failed allocations and just segment fault when the returned pointer is supposed to be NULL.
This happens because the C API functions call C++ model-editing related code that seems to rely on throwing exceptions to indicate memory-allocation failure.
I know this is an edge case that can happen on fairly large number of items (or memory-constrained environments), but I'd still like to have an option in the API to control it, instead of just segment faulting due to C++'s exceptions trying to go through C (and in my case Rust) code.
### Steps for reproduction
1. Compile the below code with `gcc -O0 -std=c23 -I mujoco-3.13.0/include -I mujoco-3.13.0/include/mujoco main.c -o ./crash_mujoco -L mujoco-3.13.0/lib -lmujoco -Wl,-rpath,$PWD/mujoco-3.13.0/lib`
2. Run the code.
### Minimal model for reproduction
Not relevant. The bug is in model-editing (procedural generation) .
### Code required for reproduction
In this "minimal" example, `malloc` is overridden with an implementation that will return `NULL` after the first `mjs_addBody` call, for the purpose of demonstration.
```c
#include
#include
#include "mujoco.h"
// Real allocator
void* __libc_malloc(size_t size);
// Override malloc.
static size_t total = 0;
static size_t limit = UINT32_MAX;
void* malloc(size_t size) {
if (total + size >= limit) {
return NULL;
}
total += size;
return __libc_malloc(size);
}
int main() {
mjSpec* spec = mj_makeSpec();
mjsBody* world = mjs_findBody(spec, "world");
for (long i = 1;; i++) {
mjsBody* body = mjs_addBody(world, NULL);
printf("Added bodies: %ld\n", i);
// Hack to deterministically fail malloc.
limit = total; // Force limit to the current size
//////////////////////////////////////////////////////
if (body == NULL) {
printf("mjs_addBody returned NULL at body %ld\n", i);
return 0;
}
}
}
```
### Confirmations
- [x] I searched the [latest documentation](https://mujoco.readthedocs.io/en/latest/overview.html) thoroughly before posting.
- [x] I searched previous [Issues](https://github.com/google-deepmind/mujoco/issues) and [Discussions](https://github.com/google-deepmind/mujoco/discussions), I am certain this has not been raised before.
Contributor guide
Research direction
Start at mjs_addBody in src/user/user_api.cc and reproduce the allocation failure with the C example from the issue. Trace how model-editing allocation failures cross the C API boundary; done means callers can handle a failed allocation without a C++ exception escaping or causing a segmentation fault.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- c, cpp
- Domain
- api
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Active
- Clarity
- Mostly clear
- Newbie friendliness
- 52/100