[ruby/json] fbuffer.c: add debug mode with bound checks.
This would have caught https://github.com/ruby/json/pull/808 on CI. https://github.com/ruby/json/commit/8109421fb4
This commit is contained in:
parent
f171a263f7
commit
212213a552
@ -36,6 +36,12 @@ typedef unsigned char _Bool;
|
|||||||
# define MAYBE_UNUSED(x) x
|
# define MAYBE_UNUSED(x) x
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef RUBY_DEBUG
|
||||||
|
#ifndef JSON_DEBUG
|
||||||
|
#define JSON_DEBUG RUBY_DEBUG
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
enum fbuffer_type {
|
enum fbuffer_type {
|
||||||
FBUFFER_HEAP_ALLOCATED = 0,
|
FBUFFER_HEAP_ALLOCATED = 0,
|
||||||
FBUFFER_STACK_ALLOCATED = 1,
|
FBUFFER_STACK_ALLOCATED = 1,
|
||||||
@ -46,6 +52,9 @@ typedef struct FBufferStruct {
|
|||||||
unsigned long initial_length;
|
unsigned long initial_length;
|
||||||
unsigned long len;
|
unsigned long len;
|
||||||
unsigned long capa;
|
unsigned long capa;
|
||||||
|
#ifdef JSON_DEBUG
|
||||||
|
unsigned long requested;
|
||||||
|
#endif
|
||||||
char *ptr;
|
char *ptr;
|
||||||
VALUE io;
|
VALUE io;
|
||||||
} FBuffer;
|
} FBuffer;
|
||||||
@ -74,6 +83,20 @@ static void fbuffer_stack_init(FBuffer *fb, unsigned long initial_length, char *
|
|||||||
fb->ptr = stack_buffer;
|
fb->ptr = stack_buffer;
|
||||||
fb->capa = stack_buffer_size;
|
fb->capa = stack_buffer_size;
|
||||||
}
|
}
|
||||||
|
#ifdef JSON_DEBUG
|
||||||
|
fb->requested = 0;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void fbuffer_consumed(FBuffer *fb, unsigned long consumed)
|
||||||
|
{
|
||||||
|
#ifdef JSON_DEBUG
|
||||||
|
if (consumed > fb->requested) {
|
||||||
|
rb_bug("fbuffer: Out of bound write");
|
||||||
|
}
|
||||||
|
fb->requested = 0;
|
||||||
|
#endif
|
||||||
|
fb->len += consumed;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void fbuffer_free(FBuffer *fb)
|
static void fbuffer_free(FBuffer *fb)
|
||||||
@ -137,6 +160,10 @@ static void fbuffer_do_inc_capa(FBuffer *fb, unsigned long requested)
|
|||||||
|
|
||||||
static inline void fbuffer_inc_capa(FBuffer *fb, unsigned long requested)
|
static inline void fbuffer_inc_capa(FBuffer *fb, unsigned long requested)
|
||||||
{
|
{
|
||||||
|
#ifdef JSON_DEBUG
|
||||||
|
fb->requested = requested;
|
||||||
|
#endif
|
||||||
|
|
||||||
if (RB_UNLIKELY(requested > fb->capa - fb->len)) {
|
if (RB_UNLIKELY(requested > fb->capa - fb->len)) {
|
||||||
fbuffer_do_inc_capa(fb, requested);
|
fbuffer_do_inc_capa(fb, requested);
|
||||||
}
|
}
|
||||||
@ -147,15 +174,22 @@ static void fbuffer_append(FBuffer *fb, const char *newstr, unsigned long len)
|
|||||||
if (len > 0) {
|
if (len > 0) {
|
||||||
fbuffer_inc_capa(fb, len);
|
fbuffer_inc_capa(fb, len);
|
||||||
MEMCPY(fb->ptr + fb->len, newstr, char, len);
|
MEMCPY(fb->ptr + fb->len, newstr, char, len);
|
||||||
fb->len += len;
|
fbuffer_consumed(fb, len);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Appends a character into a buffer. The buffer needs to have sufficient capacity, via fbuffer_inc_capa(...). */
|
/* Appends a character into a buffer. The buffer needs to have sufficient capacity, via fbuffer_inc_capa(...). */
|
||||||
static inline void fbuffer_append_reserved_char(FBuffer *fb, char chr)
|
static inline void fbuffer_append_reserved_char(FBuffer *fb, char chr)
|
||||||
{
|
{
|
||||||
|
#ifdef JSON_DEBUG
|
||||||
|
if (fb->requested < 1) {
|
||||||
|
rb_bug("fbuffer: unreserved write");
|
||||||
|
}
|
||||||
|
fb->requested--;
|
||||||
|
#endif
|
||||||
|
|
||||||
fb->ptr[fb->len] = chr;
|
fb->ptr[fb->len] = chr;
|
||||||
fb->len += 1;
|
fb->len++;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void fbuffer_append_str(FBuffer *fb, VALUE str)
|
static void fbuffer_append_str(FBuffer *fb, VALUE str)
|
||||||
@ -172,7 +206,7 @@ static inline void fbuffer_append_char(FBuffer *fb, char newchr)
|
|||||||
{
|
{
|
||||||
fbuffer_inc_capa(fb, 1);
|
fbuffer_inc_capa(fb, 1);
|
||||||
*(fb->ptr + fb->len) = newchr;
|
*(fb->ptr + fb->len) = newchr;
|
||||||
fb->len++;
|
fbuffer_consumed(fb, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline char *fbuffer_cursor(FBuffer *fb)
|
static inline char *fbuffer_cursor(FBuffer *fb)
|
||||||
@ -182,7 +216,7 @@ static inline char *fbuffer_cursor(FBuffer *fb)
|
|||||||
|
|
||||||
static inline void fbuffer_advance_to(FBuffer *fb, char *end)
|
static inline void fbuffer_advance_to(FBuffer *fb, char *end)
|
||||||
{
|
{
|
||||||
fb->len = end - fb->ptr;
|
fbuffer_consumed(fb, (end - fb->ptr) - fb->len);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -4,8 +4,9 @@ if RUBY_ENGINE == 'truffleruby'
|
|||||||
# The pure-Ruby generator is faster on TruffleRuby, so skip compiling the generator extension
|
# The pure-Ruby generator is faster on TruffleRuby, so skip compiling the generator extension
|
||||||
File.write('Makefile', dummy_makefile("").join)
|
File.write('Makefile', dummy_makefile("").join)
|
||||||
else
|
else
|
||||||
append_cflags("-std=c99")
|
append_cflags("-std=c99 -O0")
|
||||||
$defs << "-DJSON_GENERATOR"
|
$defs << "-DJSON_GENERATOR"
|
||||||
|
$defs << "-DJSON_DEBUG" if ENV["JSON_DEBUG"]
|
||||||
|
|
||||||
if enable_config('generator-use-simd', default=!ENV["JSON_DISABLE_SIMD"])
|
if enable_config('generator-use-simd', default=!ENV["JSON_DISABLE_SIMD"])
|
||||||
if RbConfig::CONFIG['host_cpu'] =~ /^(arm.*|aarch64.*)/
|
if RbConfig::CONFIG['host_cpu'] =~ /^(arm.*|aarch64.*)/
|
||||||
|
@ -404,7 +404,7 @@ static inline unsigned char search_escape_basic_neon(search_state *search)
|
|||||||
if (!mask) {
|
if (!mask) {
|
||||||
// Nothing to escape, ensure search_flush doesn't do anything by setting
|
// Nothing to escape, ensure search_flush doesn't do anything by setting
|
||||||
// search->cursor to search->ptr.
|
// search->cursor to search->ptr.
|
||||||
search->buffer->len += remaining;
|
fbuffer_consumed(search->buffer, remaining);
|
||||||
search->ptr = search->end;
|
search->ptr = search->end;
|
||||||
search->cursor = search->end;
|
search->cursor = search->end;
|
||||||
return 0;
|
return 0;
|
||||||
@ -511,7 +511,7 @@ static inline TARGET_SSE2 FORCE_INLINE unsigned char search_escape_basic_sse2(se
|
|||||||
if (needs_escape_mask == 0) {
|
if (needs_escape_mask == 0) {
|
||||||
// Nothing to escape, ensure search_flush doesn't do anything by setting
|
// Nothing to escape, ensure search_flush doesn't do anything by setting
|
||||||
// search->cursor to search->ptr.
|
// search->cursor to search->ptr.
|
||||||
search->buffer->len += remaining;
|
fbuffer_consumed(search->buffer, remaining);
|
||||||
search->ptr = search->end;
|
search->ptr = search->end;
|
||||||
search->cursor = search->end;
|
search->cursor = search->end;
|
||||||
return 0;
|
return 0;
|
||||||
@ -1415,7 +1415,7 @@ static void generate_json_float(FBuffer *buffer, struct generate_json_data *data
|
|||||||
/* fpconv_dtoa converts a float to its shortest string representation,
|
/* fpconv_dtoa converts a float to its shortest string representation,
|
||||||
* but it adds a ".0" if this is a plain integer.
|
* but it adds a ".0" if this is a plain integer.
|
||||||
*/
|
*/
|
||||||
buffer->len += len;
|
fbuffer_consumed(buffer, len);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void generate_json_fragment(FBuffer *buffer, struct generate_json_data *data, VALUE obj)
|
static void generate_json_fragment(FBuffer *buffer, struct generate_json_data *data, VALUE obj)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user