I'm working on a Javascript/web module bundler program that is written in C and a bit of JavaScript. I keep having a problem with the program where the program will randomly crash at a random malloc call in a random function. It only occurs when I use it on a more complicated project with far more files and will work fine if used on a smaller project. I've had errors with malloc
and free
that were being caused by memory leaks/errors in other parts of the program but as far as I can tell there is nothing wrong this time that is noticeable.
This is the error message that occurs:
RuntimeError: memory access out of bounds
at dlmalloc (wasm://wasm/007a3c2a:wasm-function[339]:0x7db59)
at ReadDataFromFile (wasm://wasm/007a3c2a:wasm-function[114]:0x4cfd8)
at BundleFile (wasm://wasm/007a3c2a:wasm-function[128]:0x55e36)
at BundleFiles (wasm://wasm/007a3c2a:wasm-function[130]:0x6b9fb)
at /mnt/c/Users/redstarbird/Documents/Projects/ArrowStack/Build/CFunctions.js:985:22
at Object.ccall (/mnt/c/Users/redstarbird/Documents/Projects/ArrowStack/Build/CFunctions.js:5328:22)
at /mnt/c/Users/redstarbird/Documents/Projects/ArrowStack/src/ArrowPack.js:104:24
The function that it crashes in looks like this:
char EMSCRIPTEN_KEEPALIVE *ReadDataFromFile(char *path)
{ // returns contents of file
printf("path: %s\n", path);
FILE *filePTR = fopen(path, "r");
if (filePTR == NULL)
{
printf("Error opening file %s\n", path);
return NULL;
}
fseek(filePTR, 0, SEEK_END); // seek to end of file
long int length = ftell(filePTR); // get length of file
fseek(filePTR, 0, SEEK_SET); // go back to start of file
char *buffer = malloc(length + 1); // This is where it crashes
if (buffer == NULL)
{
printf("Error creating buffer\n");
exit(1);
}
int currentChar = 0;
do
{
if (feof(filePTR))
{
break;
}
buffer[currentChar] = fgetc(filePTR);
currentChar++;
} while (1);
fclose(filePTR);
buffer[currentChar - 1] = '\0';
return buffer;
}
The project is compiled using this command:
emcc --no-entry -s INITIAL_MEMORY=128mb -Wl,--export=__heap_base -Wl,--export=__data_end -Wl,--export=malloc -Wl,--export=free -sENVIRONMENT=node --profiling -sRUNTIME_DEBUG=1 -fsanitize=undefined -sLLD_REPORT_UNDEFINED -g3 -sSTACK_OVERFLOW_CHECK=2 -sASSERTIONS=2 src/Main.c src/C/cJSON/cJSON.c src/DependencyGraph/DependencyGraph.c ./src/C/StringRelatedFunctions.c ./src/Regex/RegexFunctions.c ./src/DependencyGraph/FindDependencies.c ./src/SettingsSingleton/settingsSingleton.c ./src/C/ProblemHandler.c ./src/C/TextColors.c ./src/C/FileHandler.c ./src/C/IntFunctions.c ./src/Minifiers/HTMLMinifier.c ./src/C/FileTypesHandler.c ./src/C/Stack.c ./src/C/BundleFiles.c ./src/C/ProgressBar.c ./src/C/StringShiftHandler.c ./src/Minifiers/JSMinifier.c -s EXPORT_ES6=0 -s MODULARIZE -s USE_ES6_IMPORT_META=0 -s EXPORTED_RUNTIME_METHODS=["ccall"] -s NODERAWFS=1 -sBINARYEN=1 -sEXIT_RUNTIME=1 -sALLOW_MEMORY_GROWTH -o Build/CFunctions.js
The javascript code to initialise the webassembly looks like this:
#!/usr/bin/env node
"use strict";
// js wrapper for arrowpack for NPM
const fs = require("fs");
const path = require("path");
const chalk = require("chalk");
const settingsSingleton = require("./SettingsSingleton/settingsSingleton");
const DirFunctions = require("./js/DirFunctions");
const wasm_exec = require("../Build/wasm_exec.js");
const CFunctionFactory = require("../Build/CFunctions.js");
const go = new Go();
// const Sleep = require("../src/js/Sleep");
const { mkdirIfNotExists } = require("./js/DirFunctions.js");
var StartTime = performance.now();
const argv = require("yargs/yargs")(process.argv.slice(2))
.option("c", {
alias: "config-path",
describe: "Path to config file if not in working directory",
type: "string"
})
.help().argv;
var CONFIG_FILE_NAME = "ArrowPack-config.json"
if (argv.c) { CONFIG_FILE_NAME = path.join(argv.c, CONFIG_FILE_NAME) } else { console.log("no custom file thingy"); }
var rawconfigData = null;
if (fs.existsSync(CONFIG_FILE_NAME)) { rawconfigData = fs.readFileSync(CONFIG_FILE_NAME, "utf8"); }
var temp;
const Settings = new settingsSingleton(rawconfigData);
if (Settings.getValue("largeProject") === false) {
temp = DirFunctions.RecursiveWalkDir(Settings.getValue("entry")); // eventually add pluginAPI event here
} else {
let RecursiveWalkDirWASM = fs.readFileSync("../Build/RecursiveWalkDir.wasm"); const { WebAssembly } = require("wasi");
let compiledWalkDirWASM = WebAssembly.compile(wasm);
let InstanceWalkDirWASM = WebAssembly.instantiate(compiledWalkDirWASM);
const { InstanceWalkDirWASMExports } = instance;
temp = InstanceWalkDirWASMExports.walk_dir(Settings.getValue("entry"));
}
var WalkedFiles = temp.Files;
var WalkedDirs = temp.Directories;
console.log(WalkedDirs);
if (WalkedDirs) {
WalkedDirs.forEach(Dir => {
console.log(chalk.red(Dir));
var tempDir = Settings.getValue("exit") + Dir.substring(Settings.getValue("entry").length);
console.log(chalk.yellowBright(tempDir));
DirFunctions.mkdirIfNotExists(tempDir);
//DirFunctions.mkdirIfNotExists(Dir);
});
}
DirFunctions.mkdirIfNotExists("ARROWPACK_TEMP_PREPROCESS_DIR");
var AbsoluteFilesCharLength = 0;
var WrappedWalkedFiles = "";
if (WalkedFiles && WalkedFiles.length > 0) { // Paths are wrapped into one string because passing array of strings from JS to C is complicated
WalkedFiles.forEach(FilePath => { WrappedWalkedFiles += FilePath + "::"; console.log(chalk.bold.blue(FilePath)); AbsoluteFilesCharLength += FilePath.length; });
console.log(chalk.red(WalkedFiles.length));
var StructsPointer;
CFunctionFactory().then((CFunctions) => {
CFunctions._CheckWasm();
CFunctions._InitFileTypes();
for (let k in Settings.settings) {
if (CFunctions.ccall(
"SendSettingsString",
"number",
["string"],
[k]
) != 1) {
throw "Error sending Wasm settings string: " + k;
}
console.log(chalk.bold.blue(k));
// Gives time to apply settings
if (CFunctions.ccall(
"SendSettingsString",
"number",
["string"],
[Settings.settings[k].toString()]
) != 1) {
throw "Error sending Wasm settings string: " + Settings.settings[k];
}
console.log(chalk.bold.blue(Settings.settings[k]));
// Also gives time to apply settings
}
var Success;
// StructsPointer = CFunctions._CreateTree(allocateUTF8(WrappedWalkedFiles), WalkedFiles.length, AbsoluteFilesCharLength); // Need to get this working eventually for faster speed but couldn't work out allocateUTF8
StructsPointer = CFunctions.ccall(
"CreateGraph",
"number",
["string", "number"],
[WrappedWalkedFiles, WalkedFiles.length]
);
Success = CFunctions.ccall(
"BundleFiles",
"number",
["number"],
[StructsPointer]
);
if (Success === 1 || Success === 0) {
fs.rm("ARROWPACK_TEMP_PREPROCESS_DIR", { recursive: true }, (err) => {
if (err) { console.error(err); } else {
console.log("Sucessfully removed temporary preprocess directory");
}
});
DirFunctions.DeleteDirectory(); //CFunctions.ccall("PrintTimeTaken", "void", ["number", "number"], [StartTime, performance.now()]); // Not working for some reason
console.log(chalk.magentaBright("Bundling files completed in " + (performance.now() - StartTime) / 1000 + " seconds"));
}
});
// console.log("\n\nBuild completed in" + (EndTime - StartTime) / 1000 + " seconds!\n\n\n"); // Need to get Wasm code to run this because Wasm code seems to be non-blocking
/*
WebAssembly.instantiateStreaming(DependencyTreeWasmBuffer, DependencyTreeMemory).then((instance) => {
StructsPointer = instance.ccall(
"CreateTree",
"number",
["string", "number", "string"],
[WrappedWalkedFiles, WalkedFiles.length, settings.getValue("entry"), settings.getValue("exit")]
)
});
var GoWASMFileHandler;
const goWASM = fs.readFileSync("../Build/FileHandler.wasm");
WebAssembly.instantiate(goWASM, go.importObject).then(function (obj) {
GoWASMFileHandler = obj.instance;
go.run(GoWASMFileHandler);
GoWASMFileHandler.exports.HandleFiles(StructsPointer, settings.getValue("entry"));
});*/
}
The full code for the project is at https://github.com/redstarbird/arrowpack. Any help would be appreciated because I'm very stuck as to how to fix this.