summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--include/urweb.h5
-rw-r--r--src/c/urweb.c135
2 files changed, 113 insertions, 27 deletions
diff --git a/include/urweb.h b/include/urweb.h
index 51f1407d..54c73c09 100644
--- a/include/urweb.h
+++ b/include/urweb.h
@@ -21,7 +21,7 @@ void uw_prune_clients(uw_context);
failure_kind uw_initialize(uw_context);
uw_context uw_init(void);
-void uw_set_app(uw_context, uw_app*);
+int uw_set_app(uw_context, uw_app*);
void uw_set_db(uw_context, void*);
void *uw_get_db(uw_context);
void uw_free(uw_context);
@@ -257,4 +257,7 @@ uw_Basis_char uw_Basis_toupper(uw_context, uw_Basis_char);
uw_Basis_string uw_Basis_currentUrl(uw_context);
void uw_set_currentUrl(uw_context, char *);
+extern size_t uw_messages_max, uw_clients_max, uw_headers_max, uw_page_max, uw_heap_max, uw_script_max;
+extern size_t uw_inputs_max, uw_cleanup_max, uw_subinputs_max, uw_deltas_max, uw_transactionals_max, uw_globals_max;
+
#endif
diff --git a/src/c/urweb.c b/src/c/urweb.c
index 1bfb3cd5..a76497bb 100644
--- a/src/c/urweb.c
+++ b/src/c/urweb.c
@@ -9,6 +9,7 @@
#include <stdarg.h>
#include <assert.h>
#include <ctype.h>
+#include <stdint.h>
#include <sys/types.h>
#include <sys/socket.h>
@@ -53,10 +54,12 @@ int uw_really_write(int fd, const void *buf, size_t len) {
// Buffers
typedef struct {
+ size_t max;
char *start, *front, *back;
} buf;
-static void buf_init(buf *b, size_t s) {
+static void buf_init(size_t max, buf *b, size_t s) {
+ b->max = max;
b->front = b->start = malloc(s);
b->back = b->front + s;
}
@@ -69,7 +72,7 @@ static void buf_reset(buf *b) {
b->front = b->start;
}
-static void buf_check(buf *b, size_t extra) {
+static int buf_check(buf *b, size_t extra) {
if (b->back - b->front < extra) {
size_t desired = b->front - b->start + extra, next;
char *new_heap;
@@ -79,11 +82,26 @@ static void buf_check(buf *b, size_t extra) {
next = 1;
for (; next < desired; next *= 2);
+ if (next > b->max)
+ if (desired <= b->max)
+ next = desired;
+ else
+ return 1;
+
new_heap = realloc(b->start, next);
b->front = new_heap + (b->front - b->start);
b->back = new_heap + next;
b->start = new_heap;
}
+
+ return 0;
+}
+
+__attribute__((noreturn)) void uw_error(uw_context, failure_kind, const char *, ...);
+
+static void ctx_buf_check(uw_context ctx, const char *kind, buf *b, size_t extra) {
+ if (buf_check(b, extra))
+ uw_error(ctx, FATAL, "Memory limit exceeded (%s)", kind);
}
static size_t buf_used(buf *b) {
@@ -94,8 +112,20 @@ static size_t buf_avail(buf *b) {
return b->back - b->start;
}
-static void buf_append(buf *b, const char *s, size_t len) {
- buf_check(b, len+1);
+static int buf_append(buf *b, const char *s, size_t len) {
+ if (buf_check(b, len+1))
+ return 1;
+
+ memcpy(b->front, s, len);
+ b->front += len;
+ *b->front = 0;
+
+ return 0;
+}
+
+static void ctx_buf_append(uw_context ctx, const char *kind, buf *b, const char *s, size_t len) {
+ ctx_buf_check(ctx, kind, b, len+1);
+
memcpy(b->front, s, len);
b->front += len;
*b->front = 0;
@@ -129,6 +159,9 @@ static unsigned n_clients;
static pthread_mutex_t clients_mutex = PTHREAD_MUTEX_INITIALIZER;
+size_t uw_messages_max = SIZE_MAX;
+size_t uw_clients_max = SIZE_MAX;
+
static client *new_client() {
client *c;
@@ -138,6 +171,8 @@ static client *new_client() {
c = clients_free;
clients_free = clients_free->next;
}
+ else if (n_clients >= uw_clients_max)
+ return NULL;
else {
++n_clients;
clients = realloc(clients, sizeof(client) * n_clients);
@@ -145,7 +180,7 @@ static client *new_client() {
c->id = n_clients-1;
pthread_mutex_init(&c->lock, NULL);
pthread_mutex_init(&c->pull_lock, NULL);
- buf_init(&c->msgs, 0);
+ buf_init(uw_messages_max, &c->msgs, 0);
clients[n_clients-1] = c;
}
@@ -280,8 +315,8 @@ static void client_send(client *c, buf *msg) {
c->send(c->sock, msg->start, buf_used(msg));
c->close(c->sock);
c->sock = -1;
- } else
- buf_append(&c->msgs, msg->start, buf_used(msg));
+ } else if (buf_append(&c->msgs, msg->start, buf_used(msg)))
+ fprintf(stderr, "Client message buffer size exceeded");
pthread_mutex_unlock(&c->lock);
}
@@ -395,6 +430,11 @@ struct uw_context {
char error_message[ERROR_BUF_LEN];
};
+size_t uw_headers_max = SIZE_MAX;
+size_t uw_page_max = SIZE_MAX;
+size_t uw_heap_max = SIZE_MAX;
+size_t uw_script_max = SIZE_MAX;
+
uw_context uw_init() {
uw_context ctx = malloc(sizeof(struct uw_context));
@@ -403,11 +443,11 @@ uw_context uw_init() {
ctx->get_header = NULL;
ctx->get_header_data = NULL;
- buf_init(&ctx->outHeaders, 0);
- buf_init(&ctx->page, 0);
+ buf_init(uw_headers_max, &ctx->outHeaders, 0);
+ buf_init(uw_page_max, &ctx->page, 0);
ctx->returning_indirectly = 0;
- buf_init(&ctx->heap, 0);
- buf_init(&ctx->script, 1);
+ buf_init(uw_heap_max, &ctx->heap, 0);
+ buf_init(uw_script_max, &ctx->script, 1);
ctx->script.start[0] = 0;
ctx->inputs = malloc(0);
@@ -447,10 +487,15 @@ uw_context uw_init() {
return ctx;
}
-void uw_set_app(uw_context ctx, uw_app *app) {
+size_t uw_inputs_max = SIZE_MAX;
+
+int uw_set_app(uw_context ctx, uw_app *app) {
ctx->app = app;
if (app && app->inputs_len > ctx->sz_inputs) {
+ if (app->inputs_len > uw_inputs_max)
+ return 1;
+
ctx->sz_inputs = app->inputs_len;
ctx->inputs = realloc(ctx->inputs, ctx->sz_inputs * sizeof(input));
memset(ctx->inputs, 0, ctx->sz_inputs * sizeof(input));
@@ -555,6 +600,8 @@ __attribute__((noreturn)) void uw_error(uw_context ctx, failure_kind fk, const c
longjmp(ctx->jmp_buf, fk);
}
+size_t uw_cleanup_max = SIZE_MAX;
+
void uw_push_cleanup(uw_context ctx, void (*func)(void *), void *arg) {
if (ctx->cleanup_front >= ctx->cleanup_back) {
int len = ctx->cleanup_back - ctx->cleanup, newLen;
@@ -562,6 +609,13 @@ void uw_push_cleanup(uw_context ctx, void (*func)(void *), void *arg) {
newLen = 1;
else
newLen = len * 2;
+
+ if (newLen > uw_cleanup_max)
+ if (len+1 <= uw_cleanup_max)
+ newLen = uw_cleanup_max;
+ else
+ uw_error(ctx, FATAL, "Exceeded limit on number of cleanup handlers");
+
ctx->cleanup = realloc(ctx->cleanup, newLen * sizeof(cleanup));
ctx->cleanup_front = ctx->cleanup + len;
ctx->cleanup_back = ctx->cleanup + newLen;
@@ -599,6 +653,10 @@ void uw_login(uw_context ctx) {
}
} else {
client *c = new_client();
+
+ if (c == NULL)
+ uw_error(ctx, FATAL, "Limit exceeded on number of message-passing clients");
+
use_client(c);
ctx->client = c;
}
@@ -670,11 +728,16 @@ static void adjust_input(input *x, input *old_start, input *new_start, size_t le
}
}
+size_t uw_subinputs_max = SIZE_MAX;
+
static input *check_input_space(uw_context ctx, size_t len) {
size_t i;
input *r;
if (ctx->used_subinputs + len >= ctx->n_subinputs) {
+ if (ctx->used_subinputs + len > uw_subinputs_max)
+ uw_error(ctx, FATAL, "Exceeded limit on number of subinputs");
+
input *new_subinputs = realloc(ctx->subinputs, sizeof(input) * (ctx->used_subinputs + len));
size_t offset = new_subinputs - ctx->subinputs;
@@ -1047,7 +1110,7 @@ void uw_set_needs_sig(uw_context ctx, int n) {
}
-static void buf_check_ctx(uw_context ctx, buf *b, size_t extra, const char *desc) {
+static void buf_check_ctx(uw_context ctx, const char *kind, buf *b, size_t extra, const char *desc) {
if (b->back - b->front < extra) {
size_t desired = b->front - b->start + extra, next;
char *new_heap;
@@ -1057,6 +1120,12 @@ static void buf_check_ctx(uw_context ctx, buf *b, size_t extra, const char *desc
next = 1;
for (; next < desired; next *= 2);
+ if (next > b->max)
+ if (desired <= b->max)
+ next = desired;
+ else
+ uw_error(ctx, FATAL, "Memory limit exceeded (%s)", kind);
+
new_heap = realloc(b->start, next);
b->front = new_heap + (b->front - b->start);
b->back = new_heap + next;
@@ -1071,7 +1140,7 @@ static void buf_check_ctx(uw_context ctx, buf *b, size_t extra, const char *desc
}
void uw_check_heap(uw_context ctx, size_t extra) {
- buf_check_ctx(ctx, &ctx->heap, extra, "heap chunk");
+ buf_check_ctx(ctx, "heap", &ctx->heap, extra, "heap chunk");
}
char *uw_heap_front(uw_context ctx) {
@@ -1163,7 +1232,7 @@ int uw_output(uw_context ctx, int (*output)(void *data, char *buf, size_t len),
}
static void uw_check_headers(uw_context ctx, size_t extra) {
- buf_check(&ctx->outHeaders, extra);
+ ctx_buf_check(ctx, "headers", &ctx->outHeaders, extra);
}
void uw_write_header(uw_context ctx, uw_Basis_string s) {
@@ -1179,7 +1248,7 @@ void uw_clear_headers(uw_context ctx) {
}
static void uw_check_script(uw_context ctx, size_t extra) {
- buf_check(&ctx->script, extra);
+ ctx_buf_check(ctx, "script", &ctx->script, extra);
}
void uw_write_script(uw_context ctx, uw_Basis_string s) {
@@ -1395,7 +1464,7 @@ uw_unit uw_Basis_set_client_source(uw_context ctx, uw_Basis_int n, uw_Basis_stri
}
static void uw_check(uw_context ctx, size_t extra) {
- buf_check(&ctx->page, extra);
+ ctx_buf_check(ctx, "page", &ctx->page, extra);
}
static void uw_writec_unsafe(uw_context ctx, char c) {
@@ -2736,6 +2805,8 @@ uw_unit uw_Basis_clear_cookie(uw_context ctx, uw_Basis_string prefix, uw_Basis_s
return uw_unit_v;
}
+size_t uw_deltas_max = SIZE_MAX;
+
static delta *allocate_delta(uw_context ctx, unsigned client) {
unsigned i;
delta *d;
@@ -2745,8 +2816,11 @@ static delta *allocate_delta(uw_context ctx, unsigned client) {
return &ctx->deltas[i];
if (ctx->used_deltas >= ctx->n_deltas) {
+ if (ctx->n_deltas + 1 > uw_deltas_max)
+ uw_error(ctx, FATAL, "Exceeded limit on number of deltas");
+
ctx->deltas = realloc(ctx->deltas, sizeof(delta) * ++ctx->n_deltas);
- buf_init(&ctx->deltas[ctx->n_deltas-1].msgs, 0);
+ buf_init(uw_messages_max, &ctx->deltas[ctx->n_deltas-1].msgs, 0);
}
d = &ctx->deltas[ctx->used_deltas++];
@@ -2772,9 +2846,9 @@ uw_unit uw_Basis_send(uw_context ctx, uw_Basis_channel chn, uw_Basis_string msg)
sprintf(pre, "%u\n%n", chn.chn, &preLen);
- buf_append(&d->msgs, pre, preLen);
- buf_append(&d->msgs, msg, len);
- buf_append(&d->msgs, "\n", 1);
+ ctx_buf_append(ctx, "messages", &d->msgs, pre, preLen);
+ ctx_buf_append(ctx, "messages", &d->msgs, msg, len);
+ ctx_buf_append(ctx, "messages", &d->msgs, "\n", 1);
return uw_unit_v;
}
@@ -2822,7 +2896,7 @@ void uw_commit(uw_context ctx) {
size_t len = strlen(ctx->script_header);
char *start = strstr(ctx->page.start, "<sc>");
if (start) {
- buf_check(&ctx->page, buf_used(&ctx->page) - 4 + len);
+ ctx_buf_check(ctx, "page", &ctx->page, buf_used(&ctx->page) - 4 + len);
start = strstr(ctx->page.start, "<sc>");
memmove(start + len, start + 4, buf_used(&ctx->page) - (start - ctx->page.start) - 3);
ctx->page.front += len - 4;
@@ -2833,7 +2907,7 @@ void uw_commit(uw_context ctx) {
size_t lenP = lenH + 40 + len;
char *start = strstr(ctx->page.start, "<sc>");
if (start) {
- buf_check(&ctx->page, buf_used(&ctx->page) - 4 + lenP);
+ ctx_buf_check(ctx, "page", &ctx->page, buf_used(&ctx->page) - 4 + lenP);
start = strstr(ctx->page.start, "<sc>");
memmove(start + lenP, start + 4, buf_used(&ctx->page) - (start - ctx->page.start) - 3);
ctx->page.front += lenP - 4;
@@ -2868,9 +2942,13 @@ int uw_rollback(uw_context ctx) {
return ctx->app->db_rollback(ctx);
}
+size_t uw_transactionals_max = SIZE_MAX;
+
void uw_register_transactional(uw_context ctx, void *data, uw_callback commit, uw_callback rollback,
uw_callback free) {
if (ctx->used_transactionals >= ctx->n_transactionals) {
+ if (ctx->used_transactionals+1 > uw_transactionals_max)
+ uw_error(ctx, FATAL, "Exceeded limit on number of transactionals");
ctx->transactionals = realloc(ctx->transactionals, sizeof(transactional) * (ctx->used_transactionals+1));
++ctx->n_transactionals;
}
@@ -3054,12 +3132,12 @@ __attribute__((noreturn)) void uw_return_blob(uw_context ctx, uw_Basis_blob b, u
uw_write_header(ctx, "Content-Type: ");
uw_write_header(ctx, mimeType);
uw_write_header(ctx, "\r\nContent-Length: ");
- buf_check(&ctx->outHeaders, INTS_MAX);
+ ctx_buf_check(ctx, "headers", &ctx->outHeaders, INTS_MAX);
sprintf(ctx->outHeaders.front, "%d%n", b.size, &len);
ctx->outHeaders.front += len;
uw_write_header(ctx, "\r\n");
- buf_append(&ctx->page, b.data, b.size);
+ ctx_buf_append(ctx, "page", &ctx->page, b.data, b.size);
for (cl = ctx->cleanup; cl < ctx->cleanup_front; ++cl)
cl->func(cl->arg);
@@ -3076,7 +3154,7 @@ __attribute__((noreturn)) void uw_redirect(uw_context ctx, uw_Basis_string url)
ctx->returning_indirectly = 1;
buf_reset(&ctx->page);
- buf_check(&ctx->page, buf_used(&ctx->outHeaders)+1);
+ ctx_buf_check(ctx, "page", &ctx->page, buf_used(&ctx->outHeaders)+1);
memcpy(ctx->page.start, ctx->outHeaders.start, buf_used(&ctx->outHeaders));
ctx->page.start[buf_used(&ctx->outHeaders)] = 0;
buf_reset(&ctx->outHeaders);
@@ -3178,6 +3256,8 @@ void *uw_get_global(uw_context ctx, char *name) {
return NULL;
}
+size_t uw_globals_max = SIZE_MAX;
+
void uw_set_global(uw_context ctx, char *name, void *data, void (*free)(void*)) {
int i;
@@ -3190,6 +3270,9 @@ void uw_set_global(uw_context ctx, char *name, void *data, void (*free)(void*))
return;
}
+ if (ctx->n_globals+1 > uw_globals_max)
+ uw_error(ctx, FATAL, "Exceeded limit on number of globals");
+
++ctx->n_globals;
ctx->globals = realloc(ctx->globals, ctx->n_globals * sizeof(global));
ctx->globals[ctx->n_globals-1].name = name;