[quickjs-devel] Re: using regular cpp with qjsc

  • From: Mario Gliewe <mag@xxxxxxxxx>
  • To: quickjs-devel@xxxxxxxxxxxxx
  • Date: Wed, 4 Sep 2019 15:57:27 +0200

shame on me,

i missed some end-of-buffer checks, leaving to core-dump on malicious
input source while whitespace-skips and parsing the numbers...

eg

    while (*p == '\t' || *p==' ')

    while (*p>='0' && *p<='9')

sorry.

greets

maG

On 04.09.19 13:45, Mario Gliewe wrote:

Hi,

while playing along with qjsc, i found it convinient to use the regular
c preprocessor when precompiling javascript to bytecode.

The problem was, that cpp would put out '#line' statements (actually: '#
nn "fname"') .

Here's a patch to make quickjs aware of these.. it will give usefull
filename/lino info in stacktraces.

caveats:

- it will leak some amount of memory for each '#line' sourceline, since
filename strings are never free()'d; i guess to avoid that, the
JSParseState's filename would have to be transformed into a JS_SYMBOL

- when having an #include inside a function, stacktraces will give wrong
filenames

greets

maG


--snip--

mag@mag-laptop:~/code/quickjs-2019-08-10$ git diff quickjs.c

diff --git a/quickjs.c b/quickjs.c
index 7bb20cb..6e14b37 100644
--- a/quickjs.c
+++ b/quickjs.c
@@ -19152,16 +19152,56 @@ static __exception int
js_parse_regexp(JSParseState *s)
 static __exception int next_token(JSParseState *s)
 {
     const uint8_t *p;
-    int c;
+    int c, lino;
     char buf[4096], *q;
     BOOL ident_has_escape;
 
     free_token(s, &s->token);
 
     p = s->last_ptr = s->buf_ptr;
-    s->got_lf = FALSE;
+
+
+    if (s->line_num>0)  // line_num==0 indicates start of parse stream
+        s->got_lf = FALSE;
+    else
+        s->line_num = 1;
+
     s->last_line_num = s->token.line_num;
  redo:
+    if (*p && s->got_lf) {
+        // parse cpp #line statements: '# nnn "fname"
+        // this might interfere with some valid ecma statements
(private names starting on new line),
+        // so we handle lines starting with whitspace as normal
javascript, and only consider '#' followed by whitespace
+        if (*p == '#' && (p[1]!=' ' && p[1] != '\t')) {
+            p++;
+            while (*p == '\t' || *p==' ')
+                p++;
+            if (*p>='0' && *p<='9') {
+                lino=0;
+                while (*p>='0' && *p<='9')
+                    lino = lino*10 + (*p++)-'0';
+                s->line_num = lino;
+                while (*p == '\t' || *p==' ')
+                    p++;
+                if (*p == '"') {
+                    p++;
+                    q=buf;
+                    while (*p && *p!='"' && *p!='\r' && *p != '\n') {
+                        if (q-buf < 4095)
+                            *q++=*p++;
+                    }
+                    *q=0;
+                    s->filename = js_strdup(s->ctx, buf);
+                }
+                while (* p && *p != '\r' && *p!='\n')
+                    p++;
+                if (*p == '\r') p++;
+                if (*p == '\n') p++;
+            }
+            goto redo;
+        }
+    }
+
     s->token.line_num = s->line_num;
     s->token.ptr = p;
     c = *p;
@@ -31084,9 +31124,10 @@ static void js_parse_init(JSContext *ctx,
JSParseState *s,
                           const char *filename)
 {
     memset(s, 0, sizeof(*s));
+    s->got_lf = TRUE;
     s->ctx = ctx;
     s->filename = filename;
-    s->line_num = 1;
+    s->line_num = 0;
     s->buf_ptr = (const uint8_t *)input;
     s->buf_end = s->buf_ptr + input_len;
     s->token.val = ' ';




Other related posts: