Coverage Report

Created: 2026-11-12 06:41

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/moddable/xs/tools/xstBundle.c
Line
Count
Source
1
/*
2
 * Copyright (c) 2016-2026  Moddable Tech, Inc.
3
 *
4
 *   This file is part of the Moddable SDK Tools.
5
 *
6
 *   The Moddable SDK Tools is free software: you can redistribute it and/or modify
7
 *   it under the terms of the GNU General Public License as published by
8
 *   the Free Software Foundation, either version 3 of the License, or
9
 *   (at your option) any later version.
10
 *
11
 *   The Moddable SDK Tools is distributed in the hope that it will be useful,
12
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
13
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14
 *   GNU General Public License for more details.
15
 *
16
 *   You should have received a copy of the GNU General Public License
17
 *   along with the Moddable SDK Tools.  If not, see <http://www.gnu.org/licenses/>.
18
 */
19
 
20
 /*
21
  N.B. Machine generated 
22
*/
23
24
/*
25
 * Fuzzilli-style bundle support.
26
 *
27
 * When Fuzzilli is run with --bundle, a single payload contains one or
28
 * more sections delimited by comment markers. Each section is one of:
29
 *
30
 *     // JS_BUNDLE_SCRIPT              -> classic script, runs immediately
31
 *     // JS_BUNDLE_MODULE:<name>       -> named module, loaded on demand
32
 *     // JS_BUNDLE_MODULE_ENTRYPOINT   -> anonymous module, runs immediately
33
 *
34
 * Scripts and the entry-point module can (dynamically or statically) import
35
 * from any named module in the same bundle. Modules are held in an in-memory
36
 * map; XS's normal loader (fxLoadModule in xst.c) is diverted here via
37
 * fxBundleLoad before it hits the filesystem.
38
 *
39
 * The same code path serves two callers:
40
 *   - xstFuzz.c, when a REPRL payload begins with a JS_BUNDLE_* marker;
41
 *   - xst.c, when the user passes --bundle on the command line (used for
42
 *     reproducing Fuzzilli-found crashes).
43
 */
44
45
#include "xsAll.h"
46
#include "xsScript.h"
47
#include "xs.h"
48
49
extern void fxRunLoop(txMachine* the);
50
51
0
#define kBundleMapCap (64)
52
0
#define kBundleEntryName "__entry__.mjs"
53
0
#define kBundleMarkerPrefix "// JS_BUNDLE_"
54
0
#define kBundleMarkerPrefixLen (13)
55
56
typedef struct {
57
  const char *name;
58
  const char *source;
59
  size_t sourceLen;
60
} txBundleEntry;
61
62
static txBundleEntry gBundleMap[kBundleMapCap];
63
static int gBundleMapCount = 0;
64
65
void fxBundleMapReset(void)
66
0
{
67
0
  gBundleMapCount = 0;
68
0
}
69
70
static void fxBundleMapPut(const char *name, const char *source, size_t sourceLen)
71
0
{
72
0
  if (gBundleMapCount >= kBundleMapCap)
73
0
    return;
74
0
  gBundleMap[gBundleMapCount].name = name;
75
0
  gBundleMap[gBundleMapCount].source = source;
76
0
  gBundleMap[gBundleMapCount].sourceLen = sourceLen;
77
0
  gBundleMapCount++;
78
0
}
79
80
static const txBundleEntry *fxBundleMapGet(const char *name)
81
0
{
82
0
  int i;
83
0
  for (i = 0; i < gBundleMapCount; i++) {
84
0
    if (0 == strcmp(gBundleMap[i].name, name))
85
0
      return &gBundleMap[i];
86
0
  }
87
0
  return NULL;
88
0
}
89
90
int fxBundleIs(const char *buffer, size_t size)
91
0
{
92
0
  return (size >= kBundleMarkerPrefixLen) &&
93
0
    (0 == memcmp(buffer, kBundleMarkerPrefix, kBundleMarkerPrefixLen));
94
0
}
95
96
// Find the next start-of-line marker at or after `from`. `bufStart` is the
97
// origin of the buffer, used to check "start of line" at position `from`.
98
static char *fxBundleNextMarker(char *from, char *end, char *bufStart)
99
0
{
100
0
  while (from < end) {
101
0
    int atLineStart = (from == bufStart) || (from > bufStart && from[-1] == '\n');
102
0
    if (atLineStart &&
103
0
        (end - from) >= kBundleMarkerPrefixLen &&
104
0
        0 == memcmp(from, kBundleMarkerPrefix, kBundleMarkerPrefixLen))
105
0
      return from;
106
0
    char *nl = memchr(from, '\n', end - from);
107
0
    if (!nl)
108
0
      return end;
109
0
    from = nl + 1;
110
0
  }
111
0
  return end;
112
0
}
113
114
// Called from xst.c's fxLoadModule. Returns 1 if the module ID was found in
115
// the bundle map (and resolved), 0 to fall through to filesystem loading.
116
// Non-bundle usage: the map is empty, so this returns 0 immediately.
117
int fxBundleLoad(txMachine *the, txSlot *module, txID moduleID)
118
112
{
119
112
  if (gBundleMapCount == 0)
120
112
    return 0;
121
0
  const char *name = fxGetKeyName(the, moduleID);
122
0
  if (!name)
123
0
    return 0;
124
0
  const txBundleEntry *entry = fxBundleMapGet(name);
125
0
  if (!entry)
126
0
    return 0;
127
0
  txStringCStream stream;
128
0
  stream.buffer = (char *)entry->source;
129
0
  stream.offset = 0;
130
0
  stream.size = entry->sourceLen;
131
0
#ifdef mxDebug
132
0
  txUnsigned flags = mxDebugFlag;
133
#else
134
  txUnsigned flags = 0;
135
#endif
136
0
  txScript *script = fxParseScript(the, &stream, fxStringCGetter, flags);
137
0
  if (script)
138
0
    fxResolveModule(the, module, moduleID, script, C_NULL, C_NULL);
139
0
  return 1;
140
0
}
141
142
void fxBundleRun(txMachine *the, char *buffer, size_t size)
143
0
{
144
0
  char *cursor = buffer;
145
0
  char *end = buffer + size;
146
0
  char *scripts[kBundleMapCap];
147
0
  size_t scriptLens[kBundleMapCap];
148
0
  int scriptCount = 0;
149
0
  const char *entrySource = NULL;
150
0
  size_t entrySourceLen = 0;
151
152
0
  fxBundleMapReset();
153
154
0
  while (cursor < end) {
155
0
    if ((end - cursor) < kBundleMarkerPrefixLen ||
156
0
        0 != memcmp(cursor, kBundleMarkerPrefix, kBundleMarkerPrefixLen)) {
157
      // stray content; skip to next marker
158
0
      cursor = fxBundleNextMarker(cursor, end, buffer);
159
0
      continue;
160
0
    }
161
0
    char *markerBody = cursor + kBundleMarkerPrefixLen;
162
0
    char *lineEnd = memchr(cursor, '\n', end - cursor);
163
0
    if (!lineEnd)
164
0
      lineEnd = end;
165
0
    char *bodyStart = (lineEnd < end) ? lineEnd + 1 : end;
166
0
    char *bodyEnd = fxBundleNextMarker(bodyStart, end, buffer);
167
0
    size_t bodyLen = bodyEnd - bodyStart;
168
169
0
    size_t markerLen = lineEnd - markerBody;
170
    // strip trailing \r if present
171
0
    if (markerLen && markerBody[markerLen - 1] == '\r')
172
0
      markerLen--;
173
174
0
    if (markerLen == 6 && 0 == memcmp(markerBody, "SCRIPT", 6)) {
175
0
      if (scriptCount < kBundleMapCap) {
176
0
        scripts[scriptCount] = bodyStart;
177
0
        scriptLens[scriptCount] = bodyLen;
178
0
        scriptCount++;
179
0
      }
180
0
    }
181
0
    else if (markerLen == 17 && 0 == memcmp(markerBody, "MODULE_ENTRYPOINT", 17)) {
182
0
      entrySource = bodyStart;
183
0
      entrySourceLen = bodyLen;
184
0
    }
185
0
    else if (markerLen > 7 && 0 == memcmp(markerBody, "MODULE:", 7)) {
186
      // null-terminate the module name in-place (overwrites the \n or \r)
187
0
      markerBody[markerLen] = '\0';
188
0
      fxBundleMapPut(markerBody + 7, bodyStart, bodyLen);
189
0
    }
190
    // else: unknown marker, skip its body
191
192
0
    cursor = bodyEnd;
193
0
  }
194
195
0
  if (entrySource)
196
0
    fxBundleMapPut(kBundleEntryName, entrySource, entrySourceLen);
197
198
0
  txSlot *realm = mxProgram.value.reference->next->value.module.realm;
199
200
  // Run classic scripts in payload order.
201
0
  int i;
202
0
  for (i = 0; i < scriptCount; i++) {
203
0
    txStringCStream stream;
204
0
    stream.buffer = scripts[i];
205
0
    stream.offset = 0;
206
0
    stream.size = scriptLens[i];
207
0
    the->script = fxParseScript(the, &stream, fxStringCGetter, mxProgramFlag | mxDebugFlag);
208
0
    if (!the->script)
209
0
      continue;
210
0
    fxRunScript(the, the->script, mxRealmGlobal(realm), C_NULL, mxRealmClosures(realm)->value.reference, C_NULL, mxProgram.value.reference);
211
0
    the->script = NULL;
212
0
    mxPullSlot(mxResult);
213
0
  }
214
215
  // Run the entry-point module via dynamic import; XS pulls dependencies
216
  // through fxLoadModule -> fxBundleLoad on demand.
217
0
  if (entrySource) {
218
0
    mxPushStringC((txString)kBundleEntryName);
219
0
    mxPushUndefined();
220
0
    fxRunImport(the, realm, C_NULL);
221
0
    mxPop();
222
0
  }
223
0
}