Skip to content

Commit

Permalink
Merge pull request #211 from brianmario/alloc_safety
Browse files Browse the repository at this point in the history
Version 1.4.2
  • Loading branch information
jhawthorn authored Apr 4, 2022
2 parents 7168bd7 + 15093d3 commit 100bf70
Show file tree
Hide file tree
Showing 14 changed files with 210 additions and 27 deletions.
6 changes: 6 additions & 0 deletions ext/yajl/api/yajl_common.h
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,12 @@ extern "C" {
# endif
#endif

#if defined(__GNUC__)
#define YAJL_WARN_UNUSED __attribute__ ((warn_unused_result))
#else
#define YAJL_WARN_UNUSED
#endif

/** pointer to a malloc function, supporting client overriding memory
* allocation routines */
typedef void * (*yajl_malloc_func)(void *ctx, unsigned int sz);
Expand Down
6 changes: 4 additions & 2 deletions ext/yajl/api/yajl_gen.h
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,9 @@ extern "C" {
* buffer to get from */
yajl_gen_no_buf,
/** Tried to decrement at depth 0 */
yajl_depth_underflow
yajl_depth_underflow,
/** Allocation error */
yajl_gen_alloc_error
} yajl_gen_status;

/** an opaque handle to a generator */
Expand Down Expand Up @@ -148,7 +150,7 @@ extern "C" {
/** access the null terminated generator buffer. If incrementally
* outputing JSON, one should call yajl_gen_clear to clear the
* buffer. This allows stream generation. */
YAJL_API yajl_gen_status yajl_gen_get_buf(yajl_gen hand,
YAJL_API YAJL_WARN_UNUSED yajl_gen_status yajl_gen_get_buf(yajl_gen hand,
const unsigned char ** buf,
unsigned int * len);

Expand Down
4 changes: 3 additions & 1 deletion ext/yajl/api/yajl_parse.h
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,9 @@ extern "C" {
yajl_status_insufficient_data,
/** An error occured during the parse. Call yajl_get_error for
* more information about the encountered error */
yajl_status_error
yajl_status_error,
/** an allocation failed */
yajl_status_alloc_failed,
} yajl_status;

/** attain a human readable, english, string for an error */
Expand Down
5 changes: 5 additions & 0 deletions ext/yajl/extconf.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,9 @@
$CFLAGS << ' -Wall -funroll-loops -Wno-declaration-after-statement'
$CFLAGS << ' -Werror-implicit-function-declaration -Wextra -O0 -ggdb3' if ENV['DEBUG']

if ENV['SANITIZE']
$CFLAGS << ' -fsanitize=address'
$LDFLAGS << ' -fsanitize=address'
end

create_makefile('yajl/yajl')
18 changes: 17 additions & 1 deletion ext/yajl/yajl.c
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,9 @@ yajl_status_to_string(yajl_status stat)
case yajl_status_error:
statStr = "parse error";
break;
case yajl_status_alloc_failed:
statStr = "allocation failed";
break;
}
return statStr;
}
Expand Down Expand Up @@ -83,6 +86,8 @@ yajl_alloc(const yajl_callbacks * callbacks,
}

hand = (yajl_handle) YA_MALLOC(afs, sizeof(struct yajl_handle_t));
if (hand == NULL)
return NULL;

/* copy in pointers to allocation routines */
memcpy((void *) &(hand->alloc), (void *) afs, sizeof(yajl_alloc_funcs));
Expand All @@ -95,23 +100,31 @@ yajl_alloc(const yajl_callbacks * callbacks,
hand->callbacks = callbacks;
hand->ctx = ctx;
hand->lexer = yajl_lex_alloc(&(hand->alloc), allowComments, validateUTF8);
if (!hand->lexer) {
YA_FREE(afs, hand);
return NULL;
}
hand->bytesConsumed = 0;
hand->decodeBuf = yajl_buf_alloc(&(hand->alloc));
yajl_bs_init(hand->stateStack, &(hand->alloc));

yajl_bs_push(hand->stateStack, yajl_state_start);
if (yajl_bs_push(hand->stateStack, yajl_state_start)) {
return NULL;
}

return hand;
}

void
yajl_reset_parser(yajl_handle hand) {
assert(hand);
hand->lexer = yajl_lex_realloc(hand->lexer);
}

void
yajl_free(yajl_handle handle)
{
assert(handle);
yajl_bs_free(handle->stateStack);
yajl_buf_free(handle->decodeBuf);
yajl_lex_free(handle->lexer);
Expand All @@ -122,6 +135,7 @@ yajl_status
yajl_parse(yajl_handle hand, const unsigned char * jsonText,
unsigned int jsonTextLen)
{
assert(hand);
yajl_status status;
status = yajl_do_parse(hand, jsonText, jsonTextLen);
return status;
Expand All @@ -130,6 +144,7 @@ yajl_parse(yajl_handle hand, const unsigned char * jsonText,
yajl_status
yajl_parse_complete(yajl_handle hand)
{
assert(hand);
/* The particular case we want to handle is a trailing number.
* Further input consisting of digits could cause our interpretation
* of the number to change (buffered "1" but "2" comes in).
Expand All @@ -143,6 +158,7 @@ unsigned char *
yajl_get_error(yajl_handle hand, int verbose,
const unsigned char * jsonText, unsigned int jsonTextLen)
{
assert(hand);
return yajl_render_error_string(hand, jsonText, jsonTextLen, verbose);
}

Expand Down
85 changes: 83 additions & 2 deletions ext/yajl/yajl_buf.c
Original file line number Diff line number Diff line change
Expand Up @@ -35,43 +35,114 @@
#include <assert.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>

#define YAJL_BUF_INIT_SIZE 2048

struct yajl_buf_t {
yajl_buf_state state;
unsigned int len;
unsigned int used;
unsigned char * data;
yajl_alloc_funcs * alloc;
};

static void *noop_realloc(void *ctx, void *ptr, unsigned int sz) {
fprintf(stderr, "Attempt to allocate on invalid yajl_buf_t\n");
abort();
}
static void *noop_malloc(void *ctx, unsigned int sz) { return noop_realloc(ctx, NULL, sz); }
static void noop_free(void *ctx, void *ptr) { }

static yajl_alloc_funcs noop_allocs = {
.malloc = &noop_malloc,
.realloc = &noop_realloc,
.free = &noop_free,
};

// A buffer to be returned if the initial allocation fails
static struct yajl_buf_t buf_alloc_error = {
.state = yajl_buf_alloc_failed,
.alloc = &noop_allocs
};

#include <stdio.h>

yajl_buf_state yajl_buf_err(yajl_buf buf)
{
assert(buf);
return buf->state;
}

static
yajl_buf_state yajl_buf_set_error(yajl_buf buf, yajl_buf_state err)
{
buf->state = err;

// free and clear all data from the buffer
YA_FREE(buf->alloc, buf->data);
buf->len = 0;
buf->data = 0;
buf->used = 0;

return err;
}

static
void yajl_buf_ensure_available(yajl_buf buf, unsigned int want)
yajl_buf_state yajl_buf_ensure_available(yajl_buf buf, unsigned int want)
{
unsigned int need;

assert(buf != NULL);

if (buf->state != yajl_buf_ok) {
return buf->state;
}

/* first call */
if (buf->data == NULL) {
buf->len = YAJL_BUF_INIT_SIZE;
buf->data = (unsigned char *) YA_MALLOC(buf->alloc, buf->len);
if (buf->data == NULL) {
return yajl_buf_set_error(buf, yajl_buf_overflow);
}

buf->data[0] = 0;
}

if (want == 0) {
return yajl_buf_ok;
}

need = buf->len;

while (want >= (need - buf->used)) need <<= 1;

// Check for overflow
if (need < buf->used) {
return yajl_buf_set_error(buf, yajl_buf_overflow);
}

if (need != buf->len) {
buf->data = (unsigned char *) YA_REALLOC(buf->alloc, buf->data, need);

if (buf->data == NULL) {
return yajl_buf_set_error(buf, yajl_buf_overflow);
}

buf->len = need;
}

return yajl_buf_ok;
}

yajl_buf yajl_buf_alloc(yajl_alloc_funcs * alloc)
{
yajl_buf b = YA_MALLOC(alloc, sizeof(struct yajl_buf_t));
if (b == NULL) {
return &buf_alloc_error;
}

memset((void *) b, 0, sizeof(struct yajl_buf_t));
b->alloc = alloc;
return b;
Expand All @@ -86,7 +157,9 @@ void yajl_buf_free(yajl_buf buf)

void yajl_buf_append(yajl_buf buf, const void * data, unsigned int len)
{
yajl_buf_ensure_available(buf, len);
if (yajl_buf_ensure_available(buf, len)) {
return;
}
if (len > 0) {
assert(data != NULL);
memcpy(buf->data + buf->used, data, len);
Expand All @@ -97,23 +170,31 @@ void yajl_buf_append(yajl_buf buf, const void * data, unsigned int len)

void yajl_buf_clear(yajl_buf buf)
{
assert(buf);
assert(!yajl_buf_err(buf));
buf->used = 0;
if (buf->data) buf->data[buf->used] = 0;
}

const unsigned char * yajl_buf_data(yajl_buf buf)
{
assert(buf);
assert(!yajl_buf_err(buf));
return buf->data;
}

unsigned int yajl_buf_len(yajl_buf buf)
{
assert(buf);
assert(!yajl_buf_err(buf));
return buf->used;
}

void
yajl_buf_truncate(yajl_buf buf, unsigned int len)
{
assert(buf);
assert(!yajl_buf_err(buf));
assert(len <= buf->used);
buf->used = len;
}
9 changes: 9 additions & 0 deletions ext/yajl/yajl_buf.h
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,12 @@
* call overhead. YMMV.
*/

typedef enum {
yajl_buf_ok = 0,
yajl_buf_alloc_failed,
yajl_buf_overflow
} yajl_buf_state;

/**
* yajl_buf is a buffer with exponential growth. the buffer ensures that
* you are always null padded.
Expand Down Expand Up @@ -77,4 +83,7 @@ unsigned int yajl_buf_len(yajl_buf buf);
YAJL_API
void yajl_buf_truncate(yajl_buf buf, unsigned int len);

/* get the state of buffer */
yajl_buf_state yajl_buf_err(yajl_buf buf);

#endif
39 changes: 28 additions & 11 deletions ext/yajl/yajl_bytestack.h
Original file line number Diff line number Diff line change
Expand Up @@ -38,9 +38,12 @@
#ifndef __YAJL_BYTESTACK_H__
#define __YAJL_BYTESTACK_H__

#include <limits.h>
#include <assert.h>
#include "api/yajl_common.h"

#define YAJL_BS_INC 128
#define YAJL_BS_MAX_SIZE UINT_MAX

typedef struct yajl_bytestack_t
{
Expand All @@ -66,20 +69,34 @@ typedef struct yajl_bytestack_t
#define yajl_bs_current(obs) \
(assert((obs).used > 0), (obs).stack[(obs).used - 1])

#define yajl_bs_push(obs, byte) { \
if (((obs).size - (obs).used) == 0) { \
(obs).size += YAJL_BS_INC; \
(obs).stack = (obs).yaf->realloc((obs).yaf->ctx,\
(void *) (obs).stack, (obs).size);\
} \
(obs).stack[((obs).used)++] = (byte); \
/* 0: success, 1: error */
static inline YAJL_WARN_UNUSED
int yajl_bs_push_inline(yajl_bytestack *obs, unsigned char byte) {
if ((obs->size - obs->used) == 0) {
if (obs->size > YAJL_BS_MAX_SIZE - YAJL_BS_INC)
return 1;
obs->size += YAJL_BS_INC;
obs->stack = obs->yaf->realloc(obs->yaf->ctx, (void *)obs->stack, obs->size);
if (!obs->stack)
return 1;
}
obs->stack[obs->used++] = byte;
return 0;
}


#define yajl_bs_push(obs, byte) yajl_bs_push_inline(&(obs), (byte))

/* removes the top item of the stack, returns nothing */
#define yajl_bs_pop(obs) { ((obs).used)--; }

#define yajl_bs_set(obs, byte) \
(obs).stack[((obs).used) - 1] = (byte);

static inline
void
yajl_bs_set_inline(yajl_bytestack *obs, unsigned char byte) {
assert(obs->used > 0);
assert(obs->size >= obs->used);
obs->stack[obs->used - 1] = byte;
}

#define yajl_bs_set(obs, byte) yajl_bs_set_inline(&obs, byte)

#endif
Loading

0 comments on commit 100bf70

Please sign in to comment.