node: bounds-check and OOM-guard in cmark_set_cstr#609
Conversation
|
Looks good. @nwellnhof any comments? |
This is by design. The allocation functions are assumed to never return NULL.
This should be fixed. It would be best to add a new macro |
Shouldn't this assumption be documented in the cmark(3) man page (which is derived from header comments in cmark.h)? |
Yes, this should be documented. |
|
@nwellnhof @jgm Thanks for the review. Pushed an updated commit addressing all feedback:
|
|
The node setters can report errors, so there's no need to abort if the input string is too large. So you could
|
|
Also squash the commits and remove mentions of "OOM guard". |
40de14c to
d36a2d9
Compare
|
@nwellnhof I have pushed an updated commit addressing all feedback:
|
| @@ -1,4 +1,5 @@ | |||
| #include <stdbool.h> | |||
| #include <stdio.h> | |||
There was a problem hiding this comment.
Adding this #include should be unnecessary now.
| new_len = (bufsize_t)srclen; | ||
| new_str = (unsigned char *)mem->realloc(NULL, (size_t)new_len + 1); | ||
| memcpy(new_str, src, (size_t)new_len + 1); |
There was a problem hiding this comment.
Casting to bufsize_t and back to size_t is confusing. Replace (size_t)new_len with srclen.
|
Also the commit message still references the old, squashed commit which is misleading.
|
5cdef44 to
fcfae7e
Compare
|
|
The commit message could be improved, but the code change looks good to me. |
fcfae7e to
c3e858e
Compare
|
@nwellnhof I have updated the commit message to follow the |
node: bounds-check in cmark_set_cstr and propagate error status
cmark_set_cstrinsrc/node.cbacks every cmark string setter —cmark_node_set_url,_set_title,_set_literal,_set_fence_info,_set_on_enter,_set_on_exit. It has two paths to undefined behaviour that I think are worth closing.1. The
reallocresult is not checked.mem->realloccan return NULL — either stdlib OOM or, more importantly, a caller-supplied allocator (cmark exposes the allocator throughcmark_node_new_with_mem). The next line dereferences it, so failure is UB insidememcpyrather than a defined abort. Other unreasonable-state paths in cmark — for example thetarget_size > INT32_MAX/2branch incmark_strbuf_grow(src/buffer.c:43-48) — alreadyabort()with a diagnostic. This change keeps that pattern.2. The
bufsize_tcast ofstrlensilently truncates.bufsize_tisint32_t(src/buffer.h:16). Forstrlen(src) > INT32_MAXthe cast wraps; subsequentlen + 1andmemcpy(*dst, src, len + 1)operate on a truncated (or, for some wraparounds, negative) count. The function then returns this incorrect length to the caller, which uses it as a size elsewhere. SameINT32_MAXbound and abort treatment as Defect 1.Verification
Built against current master (
b320f40) with-fsanitize=address,undefined. A small reproducer usingcmark_node_new_with_memto inject a failingrealloc:Before:
After this patch:
api_teston the patched build:556 tests passed, 0 failed, 0 skipped.