Skip to content

Commit efefccd

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 a329721 commit efefccd

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
@@ -8524,7 +8524,16 @@ NK_LIB void*
85248524
nk_malloc(nk_handle unused, void *old,nk_size size)
85258525
{
85268526
NK_UNUSED(unused);
8527-
NK_UNUSED(old);
8527+
/*
8528+
* Due to historical reasons, Nuklear always calls alloc(user,old,size)
8529+
* with the "old" pointer always being NULL. For full explanation, see:
8530+
* https://github.com/Immediate-Mode-UI/Nuklear/issues/768
8531+
*
8532+
* Note that this limitation is only meant for Nuklear's internal code.
8533+
* If you're wondering what to pass into nk_allocator's "alloc" field,
8534+
* you can still pass realloc there and it will work just fine.
8535+
*/
8536+
NK_ASSERT(!old && "Nuklear's internal code must never reallocate existing memory !");
85288537
return malloc(size);
85298538
}
85308539
NK_LIB void
@@ -8617,15 +8626,13 @@ nk_buffer_realloc(struct nk_buffer *b, nk_size capacity, nk_size *size)
86178626
return 0;
86188627

86198628
buffer_size = b->memory.size;
8620-
temp = b->pool.alloc(b->pool.userdata, b->memory.ptr, capacity);
8629+
temp = b->pool.alloc(b->pool.userdata, 0, capacity);
86218630
NK_ASSERT(temp);
86228631
if (!temp) return 0;
86238632

86248633
*size = capacity;
8625-
if (temp != b->memory.ptr) {
8626-
NK_MEMCPY(temp, b->memory.ptr, buffer_size);
8627-
b->pool.free(b->pool.userdata, b->memory.ptr);
8628-
}
8634+
NK_MEMCPY(temp, b->memory.ptr, buffer_size);
8635+
b->pool.free(b->pool.userdata, b->memory.ptr);
86298636

86308637
if (b->size == buffer_size) {
86318638
/* no back buffer so just set correct size */
@@ -30762,6 +30769,8 @@ nk_tooltipfv(struct nk_context *ctx, const char *fmt, va_list args)
3076230769
/// - [y]: Minor version with non-breaking API and library changes
3076330770
/// - [z]: Patch version with no direct changes to the API
3076430771
///
30772+
/// - 2026/01/27 (4.13.3) - Fix crash inside of nk_buffer_realloc and
30773+
/// forbid the use of realloc in the whole library (internally only)
3076530774
/// - 2026/01/31 (4.13.2) - Fix: replace incorrect static asserts for size(nk_bool)
3076630775
/// - 2026/01/26 (4.13.1) - Fix: nk_do_property now uses NK_STRTOD via macro
3076730776
/// - Fix: failure to build from source, due to

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.3) - Fix crash inside of nk_buffer_realloc and
11+
/// forbid the use of realloc in the whole library (internally only)
1012
/// - 2026/01/31 (4.13.2) - Fix: replace incorrect static asserts for size(nk_bool)
1113
/// - 2026/01/26 (4.13.1) - Fix: nk_do_property now uses NK_STRTOD via macro
1214
/// - Fix: failure to build from source, due to

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)