Skip to content

Commit 08bb49a

Browse files
committed
Fortify image_helpers
It was possible to crash AtomVM by using load_image with invalid data. Fortify it, so it returns an error instead. Signed-off-by: Davide Bettio <davide@uninstall.it>
1 parent eb901e7 commit 08bb49a

1 file changed

Lines changed: 98 additions & 14 deletions

File tree

image_helpers.c

Lines changed: 98 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -20,36 +20,120 @@
2020

2121
#include "image_helpers.h"
2222

23+
#include <stdio.h>
24+
#include <stdlib.h>
25+
26+
#include <defaultatoms.h>
27+
#include <globalcontext.h>
28+
#include <memory.h>
29+
#include <term.h>
30+
#include <utils.h>
31+
2332
#include "spng.h"
2433

34+
static void send_decoded_image(term ref, term pid, const void *data, size_t size, Context *ctx)
35+
{
36+
// TODO: change return format to {ok, binary}
37+
BEGIN_WITH_STACK_HEAP(TUPLE_SIZE(2) + REF_SIZE + term_binary_heap_size(size), heap);
38+
39+
term return_tuple = term_alloc_tuple(2, &heap);
40+
term_put_tuple_element(return_tuple, 0, ref);
41+
term_put_tuple_element(return_tuple, 1, term_from_literal_binary(data, size, &heap, ctx->global));
42+
43+
int local_process_id = term_to_local_process_id(pid);
44+
globalcontext_send_message(ctx->global, local_process_id, return_tuple);
45+
46+
END_WITH_STACK_HEAP(heap, ctx->global)
47+
}
48+
49+
static void send_load_image_error(term ref, term pid, term reason, Context *ctx)
50+
{
51+
BEGIN_WITH_STACK_HEAP(TUPLE_SIZE(2) + TUPLE_SIZE(2) + REF_SIZE, heap);
52+
53+
term error_tuple = term_alloc_tuple(2, &heap);
54+
term_put_tuple_element(error_tuple, 0, ERROR_ATOM);
55+
term_put_tuple_element(error_tuple, 1, reason);
56+
57+
term return_tuple = term_alloc_tuple(2, &heap);
58+
term_put_tuple_element(return_tuple, 0, ref);
59+
term_put_tuple_element(return_tuple, 1, error_tuple);
60+
61+
int local_process_id = term_to_local_process_id(pid);
62+
globalcontext_send_message(ctx->global, local_process_id, return_tuple);
63+
64+
END_WITH_STACK_HEAP(heap, ctx->global)
65+
}
66+
67+
static term invalid_image_reason(Context *ctx)
68+
{
69+
term reason = globalcontext_make_atom(ctx->global, "\xD"
70+
"invalid_image");
71+
if (UNLIKELY(term_is_invalid_term(reason))) {
72+
return ERROR_ATOM;
73+
}
74+
return reason;
75+
}
76+
2577
void handle_load_image(term req, term ref, term pid, Context *ctx)
2678
{
79+
if (UNLIKELY(term_get_tuple_arity(req) < 2)) {
80+
fprintf(stderr, "handle_load_image: missing image argument\n");
81+
send_load_image_error(ref, pid, BADARG_ATOM, ctx);
82+
return;
83+
}
84+
2785
term image_bin = term_get_tuple_element(req, 1);
86+
if (UNLIKELY(!term_is_binary(image_bin))) {
87+
fprintf(stderr, "handle_load_image: image argument is not a binary\n");
88+
send_load_image_error(ref, pid, BADARG_ATOM, ctx);
89+
return;
90+
}
91+
2892
const void *buf = term_binary_data(image_bin);
2993
size_t buf_size = term_binary_size(image_bin);
3094

3195
spng_ctx *png_ctx = spng_ctx_new(0);
32-
spng_set_png_buffer(png_ctx, buf, buf_size);
96+
if (UNLIKELY(!png_ctx)) {
97+
fprintf(stderr, "handle_load_image: spng_ctx_new failed\n");
98+
send_load_image_error(ref, pid, OUT_OF_MEMORY_ATOM, ctx);
99+
return;
100+
}
101+
102+
int ret = spng_set_png_buffer(png_ctx, buf, buf_size);
103+
if (UNLIKELY(ret != SPNG_OK)) {
104+
fprintf(stderr, "handle_load_image: spng_set_png_buffer failed: %s\n", spng_strerror(ret));
105+
spng_ctx_free(png_ctx);
106+
send_load_image_error(ref, pid, invalid_image_reason(ctx), ctx);
107+
return;
108+
}
33109

34110
size_t out_size;
35-
spng_decoded_image_size(png_ctx, SPNG_FMT_RGBA8, &out_size);
111+
ret = spng_decoded_image_size(png_ctx, SPNG_FMT_RGBA8, &out_size);
112+
if (UNLIKELY(ret != SPNG_OK)) {
113+
fprintf(stderr, "handle_load_image: spng_decoded_image_size failed: %s\n", spng_strerror(ret));
114+
spng_ctx_free(png_ctx);
115+
send_load_image_error(ref, pid, invalid_image_reason(ctx), ctx);
116+
return;
117+
}
36118

37119
void *out = malloc(out_size);
38-
spng_decode_image(png_ctx, out, out_size, SPNG_FMT_RGBA8, 0);
120+
if (UNLIKELY(!out)) {
121+
fprintf(stderr, "handle_load_image: cannot allocate %zu bytes for decoded image\n", out_size);
122+
spng_ctx_free(png_ctx);
123+
send_load_image_error(ref, pid, OUT_OF_MEMORY_ATOM, ctx);
124+
return;
125+
}
39126

127+
ret = spng_decode_image(png_ctx, out, out_size, SPNG_FMT_RGBA8, 0);
40128
spng_ctx_free(png_ctx);
129+
if (UNLIKELY(ret != SPNG_OK)) {
130+
fprintf(stderr, "handle_load_image: spng_decode_image failed: %s\n", spng_strerror(ret));
131+
free(out);
132+
send_load_image_error(ref, pid, invalid_image_reason(ctx), ctx);
133+
return;
134+
}
41135

42-
// term_binary_heap_size(out_size) is usually less than 100 bytes
43-
BEGIN_WITH_STACK_HEAP(TUPLE_SIZE(3) + term_binary_heap_size(out_size), heap);
44-
45-
term return_tuple = term_alloc_tuple(2, &heap);
46-
term_put_tuple_element(return_tuple, 0, ref);
47-
term_put_tuple_element(return_tuple, 1, term_from_literal_binary(out, out_size, &heap, ctx->global));
136+
send_decoded_image(ref, pid, out, out_size, ctx);
48137

49138
free(out);
50-
51-
int local_process_id = term_to_local_process_id(pid);
52-
globalcontext_send_message(ctx->global, local_process_id, return_tuple);
53-
54-
END_WITH_STACK_HEAP(heap, ctx->global)
55139
}

0 commit comments

Comments
 (0)