nanowasm/src/section/code.c

248 lines
5.1 KiB
C

/*
* nanowasm, a tiny WebAssembly/Wasm interpreter
* Copyright (C) 2023-2024 Xavier Del Campo Romero
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
#include <log.h>
#include <nanowasm/nw.h>
#include <opcodes.h>
#include <interp.h>
#include <sections.h>
#include <wasm_types.h>
#include <errno.h>
#include <inttypes.h>
#include <stdint.h>
#include <string.h>
static int check_body(FILE *const f, const unsigned long n)
{
unsigned long rem = n;
while (rem)
{
const long before = ftell(f);
uint8_t byte;
if (before < 0)
{
LOG("%s: ftell(3) before: %s\n", __func__, strerror(errno));
return -1;
}
else if (!fread(&byte, sizeof byte, 1, f))
{
LOG("%s: fread(3) failed, feof=%d, ferror=%d\n",
__func__, feof(f), ferror(f));
return -1;
}
else if (rem == 1 && byte != OP_END)
{
LOG("%s: unexpected opcode %#" PRIx8 " at body end\n",
__func__, byte);
return -1;
}
else if (interp_check_opcode(byte, f))
{
LOG("%s: interp_check_opcode failed\n", __func__);
return -1;
}
const long after = ftell(f);
if (after < 0)
{
LOG("%s: ftell(3) after: %s\n", __func__, strerror(errno));
return -1;
}
rem -= after - before;
}
return 0;
}
static int check_type(FILE *const f)
{
varint7 type;
if (varint7_read(f, &type))
{
LOG("%s: varint7_read failed\n", __func__);
return -1;
}
return 0;
}
static int check_local(FILE *const f)
{
varuint32 count;
if (varuint32_read(f, &count))
{
LOG("%s: varuint32_read failed\n", __func__);
return -1;
}
else if (check_type(f))
{
LOG("%s: check_type failed\n", __func__);
return -1;
}
return 0;
}
static int check_locals(FILE *const f)
{
varuint32 local_count;
if (varuint32_read(f, &local_count))
{
LOG("%s: varuint32_read local_count failed\n", __func__);
return -1;
}
for (varuint32 i = 0; i < local_count; i++)
if (check_local(f))
{
LOG("%s: check_local failed\n", __func__);
return -1;
}
return 0;
}
static int check_function_body(FILE *const f)
{
varuint32 body_size;
if (varuint32_read(f, &body_size))
{
LOG("%s: varuint32_read body_size failed\n", __func__);
return -1;
}
const long start = ftell(f);
if (start < 0)
{
LOG("%s: ftell(3) start: %s\n", __func__, strerror(errno));
return -1;
}
else if (check_locals(f))
{
LOG("%s: check_locals failed\n", __func__);
return -1;
}
const long end = ftell(f);
if (end < 0)
{
LOG("%s: ftell(3) end: %s\n", __func__, strerror(errno));
return -1;
}
const unsigned long consumed = end - start;
if (consumed > body_size)
{
LOG("%s: exceeded function body size\n", __func__);
return -1;
}
else if (check_body(f, body_size - consumed))
{
LOG("%s: check_body failed\n", __func__);
return -1;
}
/* TODO: add function body pointers to struct nanowasm. */
return 0;
}
static int check_function_bodies(FILE *const f)
{
varuint32 count;
if (varuint32_read(f, &count))
{
LOG("%s: varuint32_read failed\n", __func__);
return -1;
}
for (varuint32 i = 0; i < count; i++)
if (check_function_body(f))
{
LOG("%s: check_function_body failed\n", __func__);
return -1;
}
return 0;
}
static int check(FILE *const f, const unsigned long len)
{
const long start = ftell(f);
if (start < 0)
{
LOG("%s: ftell(3): %s\n", __func__, strerror(errno));
return -1;
}
else if (check_function_bodies(f))
{
LOG("%s: check_function_bodies failed\n", __func__);
return -1;
}
const long end = ftell(f);
if (end < 0)
{
LOG("%s: ftell(3): %s\n", __func__, strerror(errno));
return -1;
}
const unsigned long size = end - start;
if (size != len)
{
LOG("%s: size exceeded (%lu expected, got %lu)\n",
__func__, len, size);
return -1;
}
return 0;
}
int section_code_check(const struct section *const s, struct nw_mod *const m,
const unsigned long len)
{
FILE *const f = s->f;
if (m->sections.code)
{
LOG("%s: ignoring duplicate section\n", __func__);
return fseek(f, len, SEEK_CUR);
}
const long offset = ftell(f);
if (offset < 0)
{
LOG("%s: ftell(3): %s\n", __func__, strerror(errno));
return -1;
}
else if (check(f, len))
{
LOG("%s: check failed\n", __func__);
return -1;
}
m->sections.code = offset;
return 0;
}