Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
63 changes: 63 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
#https://github.com/github/gitignore/blob/main/C.gitignore
# Prerequisites
*.d

# Object files
*.o
*.ko
*.obj
*.elf

# Linker output
*.ilk
*.map
*.exp

# Precompiled Headers
*.gch
*.pch

# Libraries
*.lib
*.a
*.la
*.lo

# Shared objects (inc. Windows DLLs)
*.dll
*.so
*.so.*
*.dylib

# Executables
*.exe
*.out
*.app
*.i*86
*.x86_64
*.hex

# Debug files
*.dSYM/
*.su
*.idb
*.pdb

# Kernel Module Compile Results
*.mod*
*.cmd
.tmp_versions/
modules.order
Module.symvers
Mkfile.old
dkms.conf

# debug information files
*.dwo

# My own

build/*
.gdb_history

*.gcno
85 changes: 85 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
#Slopped together AI :(

BUILD := build
AFL_CC := afl-clang-fast
CLANG := clang-21

AFL_ENV_OPTIONS := AFL_MAP_SIZE=1048576 AFL_LLVM_INSTRUMENT=CLASSIC AFL_LLVM_CTX_K=3

SRC := main.c elk.c
CLI_SRC := cli.c elk.c
CLI_OPTIONS := -flto -ggdb3 -O0 -fno-omit-frame-pointer

.PHONY: all afl cli clean \
normal san1 san2 san3 laf cmplog \
cli-normal cli-san1 cli-san2 cli-san3

# ---- helpers ----

# Ensure build dir exists
$(BUILD):
mkdir -p $(BUILD)

# Generic AFL build macro
define AFL_BUILD
$1: | $(BUILD)
$2 $(AFL_ENV_OPTIONS) $(AFL_CC) $(SRC) -o $(BUILD)/$1
endef

# Generic CLI build macro
define CLI_BUILD
$1: | $(BUILD)
$(CLANG) $2 $(CLI_SRC) $(CLI_OPTIONS) -o $(BUILD)/$1
endef

# ---- targets ----

all: afl cli
afl: normal san1 san2 san3 laf cmplog
cli: cli-normal cli-san1 cli-san2 cli-san3

# ---- AFL variants ----

$(eval $(call AFL_BUILD,normal,))

$(eval $(call AFL_BUILD,san1, \
AFL_LLVM_ONLY_FSRV=1 \
AFL_USE_ASAN=1 \
AFL_USE_UBSAN=1 \
AFL_USE_LSAN=1))

$(eval $(call AFL_BUILD,san2, \
AFL_LLVM_ONLY_FSRV=1 \
AFL_HARDEN=1 \
AFL_USE_TSAN=1))

$(eval $(call AFL_BUILD,san3, \
AFL_LLVM_ONLY_FSRV=1 \
AFL_USE_MSAN=1 \
AFL_USE_CFISAN=1))

$(eval $(call AFL_BUILD,laf, \
AFL_LLVM_LAF_SPLIT_SWITCHES=1 \
AFL_LLVM_LAF_TRANSFORM_COMPARES=1 \
AFL_LLVM_LAF_SPLIT_COMPARES=1))

$(eval $(call AFL_BUILD,cmplog, \
AFL_LLVM_CMPLOG=1))

# ---- CLI variants ----

$(eval $(call CLI_BUILD,cli-normal, -g))

$(eval $(call CLI_BUILD,cli-san1, \
-fsanitize=address,leak,undefined))

$(eval $(call CLI_BUILD,cli-san2, \
-fsanitize=thread))

$(eval $(call CLI_BUILD,cli-san3, \
-fsanitize=memory,cfi -fvisibility=hidden))

# ---- clean ----

clean:
rm -rf $(BUILD)
40 changes: 40 additions & 0 deletions cli.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "elk.h"

int main(int argc, char *argv[]) {
if (argc != 2) {
fprintf(stderr, "Usage: %s script.js\n", argv[0]);
return 1;
}

const char *filename = argv[1];
FILE *fp = fopen(filename, "rb");
if (!fp) {
perror("fopen");
return 1;
}

fseek(fp, 0, SEEK_END);
long len = ftell(fp);
fseek(fp, 0, SEEK_SET);

char *data = malloc(len + 1);
if (!data) return 1;

fread(data, 1, len, fp);
data[len] = 0;
fclose(fp);

char mem[4096];
struct js *js = js_create(mem, sizeof(mem));
js_setmaxcss(js, 2048);

jsval_t res = js_eval(js, data, len);
printf("%s\n", js_str(js, res));

free(data);
return 0;
}
98 changes: 70 additions & 28 deletions elk.c
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,22 @@
// Alternatively, you can license this software under a commercial
// license, please contact us at https://cesanta.com/contact.html

/*
#if defined(__GNUC__) && !defined(JS_OPT) && !defined(ARDUINO_AVR_UNO) && \
!defined(ARDUINO_AVR_NANO) && !defined(ARDUINO_AVR_PRO) && \
!defined(__APPLE__)
#pragma GCC optimize("O3,inline")
#endif
*/

#if defined(__has_feature)
# if __has_feature(address_sanitizer)
# define JS_ASAN 1
# endif
# else
# define JS_ASAN 0
#endif


#include <assert.h>
#include <math.h>
Expand Down Expand Up @@ -176,10 +187,15 @@ static void setlwm(struct js *js) {
jsoff_t n = 0, css = 0;
if (js->brk < js->size) n = js->size - js->brk;
if (js->lwm > n) js->lwm = n;
if ((char *) js->cstk > (char *) &n)
css = (jsoff_t) ((char *) js->cstk - (char *) &n);

char *cur = (char *) &n;
char *base = (char *) js->cstk;
css = (jsoff_t) (cur > base ? cur - base : base - cur); // abs difference

if (css > js->css) js->css = css;
}
// printf("LWM: %u, CSS: %u\n", (unsigned) js->lwm, (unsigned) js->css);
// printf("cstk: %p, stk: %p\n", js->cstk, &n);
}

// Copy src to dst, make no overflows, 0-terminate. Return bytes copied
static size_t cpy(char *dst, size_t dstlen, const char *src, size_t srclen) {
Expand Down Expand Up @@ -615,6 +631,8 @@ static jsval_t lookup(struct js *js, const char *buf, size_t len) {
}

static jsval_t resolveprop(struct js *js, jsval_t v) {
setlwm(js);
if (js->maxcss > 0 && js->css > js->maxcss) return js_mkerr(js, "C stack");
if (vtype(v) != T_PROP) return v;
return resolveprop(js,
loadval(js, (jsoff_t) (vdata(v) + sizeof(jsoff_t) * 2)));
Expand Down Expand Up @@ -779,8 +797,42 @@ static jsval_t do_call_op(struct js *js, jsval_t func, jsval_t args) {
}

// clang-format off
#ifndef _MSC_VER
__attribute__((no_sanitize("undefined")))
#endif
static jsval_t ubsan_op(struct js *js, uint8_t op, double a, double b, jsval_t l, jsval_t r) {
switch (op) {
//case TOK_EXP: return tov(pow(a, b));
case TOK_DIV: return tod(r) == 0 ? js_mkerr(js, "div by zero") : tov(a / b);
case TOK_REM: return tod(r) == 0 ? js_mkerr(js, "rem by zero") : tov(a - b * ((double) (long) (a / b)));
case TOK_MUL: return tov(a * b);
case TOK_PLUS: return tov(a + b);
case TOK_MINUS: return tov(a - b);
case TOK_XOR: return tov((double)((long) a ^ (long) b));
case TOK_AND: return tov((double)((long) a & (long) b));
case TOK_OR: return tov((double)((long) a | (long) b));
case TOK_UMINUS: return tov(-b);
case TOK_UPLUS: return r;
case TOK_TILDA: return tov((double)(~(long) b));
case TOK_NOT: return mkval(T_BOOL, b == 0);
case TOK_SHL: return tov((double)((long) a << (long) b));
case TOK_SHR: return tov((double)((long) a >> (long) b));
case TOK_DOT: return do_dot_op(js, l, r);
case TOK_EQ: return mkval(T_BOOL, (long) a == (long) b);
case TOK_NE: return mkval(T_BOOL, (long) a != (long) b);
case TOK_LT: return mkval(T_BOOL, a < b);
case TOK_LE: return mkval(T_BOOL, a <= b);
case TOK_GT: return mkval(T_BOOL, a > b);
case TOK_GE: return mkval(T_BOOL, a >= b);
default: return js_mkerr(js, "unknown op %d", (int) op); // LCOV_EXCL_LINE
}
}

static jsval_t do_op(struct js *js, uint8_t op, jsval_t lhs, jsval_t rhs) {
if (js->flags & F_NOEXEC) return 0;
setlwm(js);
if (js->maxcss > 0 && js->css > js->maxcss) return js_mkerr(js, "C stack");

jsval_t l = resolveprop(js, lhs), r = resolveprop(js, rhs);
// printf("OP %d %d %d\n", op, vtype(lhs), vtype(r));
setlwm(js);
Expand All @@ -806,31 +858,7 @@ static jsval_t do_op(struct js *js, uint8_t op, jsval_t lhs, jsval_t rhs) {
if (is_unary(op) && vtype(r) != T_NUM) return js_mkerr(js, "type mismatch");
if (!is_unary(op) && op != TOK_DOT && (vtype(l) != T_NUM || vtype(r) != T_NUM)) return js_mkerr(js, "type mismatch");
double a = tod(l), b = tod(r);
switch (op) {
//case TOK_EXP: return tov(pow(a, b));
case TOK_DIV: return tod(r) == 0 ? js_mkerr(js, "div by zero") : tov(a / b);
case TOK_REM: return tov(a - b * ((double) (long) (a / b)));
case TOK_MUL: return tov(a * b);
case TOK_PLUS: return tov(a + b);
case TOK_MINUS: return tov(a - b);
case TOK_XOR: return tov((double)((long) a ^ (long) b));
case TOK_AND: return tov((double)((long) a & (long) b));
case TOK_OR: return tov((double)((long) a | (long) b));
case TOK_UMINUS: return tov(-b);
case TOK_UPLUS: return r;
case TOK_TILDA: return tov((double)(~(long) b));
case TOK_NOT: return mkval(T_BOOL, b == 0);
case TOK_SHL: return tov((double)((long) a << (long) b));
case TOK_SHR: return tov((double)((long) a >> (long) b));
case TOK_DOT: return do_dot_op(js, l, r);
case TOK_EQ: return mkval(T_BOOL, (long) a == (long) b);
case TOK_NE: return mkval(T_BOOL, (long) a != (long) b);
case TOK_LT: return mkval(T_BOOL, a < b);
case TOK_LE: return mkval(T_BOOL, a <= b);
case TOK_GT: return mkval(T_BOOL, a > b);
case TOK_GE: return mkval(T_BOOL, a >= b);
default: return js_mkerr(js, "unknown op %d", (int) op); // LCOV_EXCL_LINE
}
return ubsan_op(js, op, a, b, l, r);
} // clang-format on

static jsval_t js_str_literal(struct js *js) {
Expand Down Expand Up @@ -1011,6 +1039,8 @@ static jsval_t js_postfix(struct js *js) {
}

static jsval_t js_unary(struct js *js) {
setlwm(js);
if (js->maxcss > 0 && js->css > js->maxcss) return js_mkerr(js, "C stack");
if (next(js) == TOK_NOT || js->tok == TOK_TILDA || js->tok == TOK_TYPEOF ||
js->tok == TOK_MINUS || js->tok == TOK_PLUS) {
uint8_t t = js->tok;
Expand Down Expand Up @@ -1059,6 +1089,8 @@ static jsval_t js_bitwise_or(struct js *js) {
}

static jsval_t js_logical_and(struct js *js) {
setlwm(js);
if (js->maxcss > 0 && js->css > js->maxcss) return js_mkerr(js, "C stack");
jsval_t res = js_bitwise_or(js);
if (is_err(res)) return res;
uint8_t flags = js->flags;
Expand All @@ -1077,6 +1109,8 @@ static jsval_t js_logical_and(struct js *js) {
}

static jsval_t js_logical_or(struct js *js) {
setlwm(js);
if (js->maxcss > 0 && js->css > js->maxcss) return js_mkerr(js, "C stack");
jsval_t res = js_logical_and(js);
if (is_err(res)) return res;
uint8_t flags = js->flags;
Expand All @@ -1095,6 +1129,8 @@ static jsval_t js_logical_or(struct js *js) {
}

static jsval_t js_ternary(struct js *js) {
setlwm(js);
if (js->maxcss > 0 && js->css > js->maxcss) return js_mkerr(js, "C stack");
jsval_t res = js_logical_or(js);
if (next(js) == TOK_Q) {
uint8_t flags = js->flags;
Expand All @@ -1117,6 +1153,8 @@ static jsval_t js_ternary(struct js *js) {
}

static jsval_t js_assignment(struct js *js) {
setlwm(js);
if (js->maxcss > 0 && js->css > js->maxcss) return js_mkerr(js, "C stack");
RTL_BINOP(js_ternary, js_assignment,
(next(js) == TOK_ASSIGN || js->tok == TOK_PLUS_ASSIGN ||
js->tok == TOK_MINUS_ASSIGN || js->tok == TOK_MUL_ASSIGN ||
Expand All @@ -1127,6 +1165,8 @@ static jsval_t js_assignment(struct js *js) {
}

static jsval_t js_expr(struct js *js) {
setlwm(js);
if (js->maxcss > 0 && js->css > js->maxcss) return js_mkerr(js, "C stack");
return js_assignment(js);
}

Expand Down Expand Up @@ -1296,6 +1336,8 @@ static jsval_t js_return(struct js *js) {
}

static jsval_t js_stmt(struct js *js) {
setlwm(js);
if (js->maxcss > 0 && js->css > js->maxcss) return js_mkerr(js, "C stack");
jsval_t res;
// jsoff_t pos = js->pos - js->tlen;
if (js->brk > js->gct) js_gc(js);
Expand Down
4 changes: 4 additions & 0 deletions examples/cmdline/api.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
({
add : function(a, b) { return a + b; },
mul : function(a, b) { return a * b; },
})
Binary file added examples/cmdline/cli
Binary file not shown.
Loading