Skip to content

Commit d5f2d34

Browse files
Fix crash in nk_buffer_realloc and forbid the use of realloc
In short: the default implementation of nk_allocator (aka nk_malloc) is fundamentally broken, and should have used realloc instead of malloc. This led to the strange workaround in nk_buffer_realloc that would crash whenever you use custom allocator with correct realloc assumption. We cannot change nk_malloc at this point, as people could have implemented their own allocators based on it, but we can ensure that Nuklear itself will never try to reallocate memory. Fixes: #768
1 parent 25f33c8 commit d5f2d34

4 files changed

Lines changed: 30 additions & 21 deletions

File tree

demo/sdl3_renderer/nuklear_sdl3_renderer.h

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -112,16 +112,7 @@ NK_INTERN void *
112112
nk_sdl_alloc(nk_handle user, void *old, nk_size size)
113113
{
114114
NK_UNUSED(user);
115-
/* FIXME: nk_sdl_alloc should use SDL_realloc here, not SDL_malloc
116-
* but this could cause a double-free due to bug within Nuklear, see:
117-
* https://github.com/Immediate-Mode-UI/Nuklear/issues/768
118-
* */
119-
#if 0
120115
return SDL_realloc(old, size);
121-
#else
122-
NK_UNUSED(old);
123-
return SDL_malloc(size);
124-
#endif
125116
}
126117

127118
NK_INTERN void

nuklear.h

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8519,7 +8519,16 @@ NK_LIB void*
85198519
nk_malloc(nk_handle unused, void *old,nk_size size)
85208520
{
85218521
NK_UNUSED(unused);
8522-
NK_UNUSED(old);
8522+
/*
8523+
* Due to historical reasons, Nuklear always calls alloc(user,old,size)
8524+
* with the "old" pointer always being NULL. For full explanation, see:
8525+
* https://github.com/Immediate-Mode-UI/Nuklear/issues/768
8526+
*
8527+
* Note that this limitation is only meant for Nuklear's internal code.
8528+
* If you're wondering what to pass into nk_allocator's "alloc" field,
8529+
* you can still pass realloc there and it will work just fine.
8530+
*/
8531+
NK_ASSERT(!old && "Nuklear's internal code must never reallocate existing memory !");
85238532
return malloc(size);
85248533
}
85258534
NK_LIB void
@@ -8612,15 +8621,13 @@ nk_buffer_realloc(struct nk_buffer *b, nk_size capacity, nk_size *size)
86128621
return 0;
86138622

86148623
buffer_size = b->memory.size;
8615-
temp = b->pool.alloc(b->pool.userdata, b->memory.ptr, capacity);
8624+
temp = b->pool.alloc(b->pool.userdata, 0, capacity);
86168625
NK_ASSERT(temp);
86178626
if (!temp) return 0;
86188627

86198628
*size = capacity;
8620-
if (temp != b->memory.ptr) {
8621-
NK_MEMCPY(temp, b->memory.ptr, buffer_size);
8622-
b->pool.free(b->pool.userdata, b->memory.ptr);
8623-
}
8629+
NK_MEMCPY(temp, b->memory.ptr, buffer_size);
8630+
b->pool.free(b->pool.userdata, b->memory.ptr);
86248631

86258632
if (b->size == buffer_size) {
86268633
/* no back buffer so just set correct size */
@@ -30757,6 +30764,8 @@ nk_tooltipfv(struct nk_context *ctx, const char *fmt, va_list args)
3075730764
/// - [y]: Minor version with non-breaking API and library changes
3075830765
/// - [z]: Patch version with no direct changes to the API
3075930766
///
30767+
/// - 2026/01/27 (4.13.1) - Fix crash inside of nk_buffer_realloc and
30768+
/// forbid the use of realloc in the whole library (internally only)
3076030769
/// - 2026/01/26 (4.13.1) - Fix: nk_do_property now uses NK_STRTOD via macro
3076130770
/// - Fix: failure to build from source, due to
3076230771
/// nuklear_math/util.c not declaring some functions

src/CHANGELOG

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@
77
/// - [y]: Minor version with non-breaking API and library changes
88
/// - [z]: Patch version with no direct changes to the API
99
///
10+
/// - 2026/01/27 (4.13.1) - Fix crash inside of nk_buffer_realloc and
11+
/// forbid the use of realloc in the whole library (internally only)
1012
/// - 2026/01/26 (4.13.1) - Fix: nk_do_property now uses NK_STRTOD via macro
1113
/// - Fix: failure to build from source, due to
1214
/// nuklear_math/util.c not declaring some functions

src/nuklear_buffer.c

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,16 @@ NK_LIB void*
1111
nk_malloc(nk_handle unused, void *old,nk_size size)
1212
{
1313
NK_UNUSED(unused);
14-
NK_UNUSED(old);
14+
/*
15+
* Due to historical reasons, Nuklear always calls alloc(user,old,size)
16+
* with the "old" pointer always being NULL. For full explanation, see:
17+
* https://github.com/Immediate-Mode-UI/Nuklear/issues/768
18+
*
19+
* Note that this limitation is only meant for Nuklear's internal code.
20+
* If you're wondering what to pass into nk_allocator's "alloc" field,
21+
* you can still pass realloc there and it will work just fine.
22+
*/
23+
NK_ASSERT(!old && "Nuklear's internal code must never reallocate existing memory !");
1524
return malloc(size);
1625
}
1726
NK_LIB void
@@ -104,15 +113,13 @@ nk_buffer_realloc(struct nk_buffer *b, nk_size capacity, nk_size *size)
104113
return 0;
105114

106115
buffer_size = b->memory.size;
107-
temp = b->pool.alloc(b->pool.userdata, b->memory.ptr, capacity);
116+
temp = b->pool.alloc(b->pool.userdata, 0, capacity);
108117
NK_ASSERT(temp);
109118
if (!temp) return 0;
110119

111120
*size = capacity;
112-
if (temp != b->memory.ptr) {
113-
NK_MEMCPY(temp, b->memory.ptr, buffer_size);
114-
b->pool.free(b->pool.userdata, b->memory.ptr);
115-
}
121+
NK_MEMCPY(temp, b->memory.ptr, buffer_size);
122+
b->pool.free(b->pool.userdata, b->memory.ptr);
116123

117124
if (b->size == buffer_size) {
118125
/* no back buffer so just set correct size */

0 commit comments

Comments
 (0)