google-deepmind / google-deepmind/mujoco

[Model editing] Failed allocations raise C++ exceptions through the C API

Open
#3,584 3 comments 0 reactions 0 assignees View on GitHub
bug
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

Open the contributing 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

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.