Reject unreadable UCE source units
This commit is contained in:
parent
cd2fa163e6
commit
a288b539c6
@ -146,6 +146,12 @@ compiler error rather than serving the old unit indefinitely. The per-unit lock
|
|||||||
also keeps concurrent synchronous compilers from waiting across a transitive
|
also keeps concurrent synchronous compilers from waiting across a transitive
|
||||||
graph when a last complete artifact is available.
|
graph when a last complete artifact is available.
|
||||||
|
|
||||||
|
Before preprocessing, the compiler verifies that the worker can actually read
|
||||||
|
the source. An unreadable path is a compile failure with a persisted diagnostic;
|
||||||
|
it never becomes an apparently valid empty side module. Source signatures mark
|
||||||
|
unreadable inputs, so correcting access invalidates that failure and permits a
|
||||||
|
normal retry without changing signatures for ordinary readable files.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 4. The workspace runtime
|
## 4. The workspace runtime
|
||||||
@ -351,7 +357,9 @@ header free-functions are `inline`. The wasm backend exposes only declarations
|
|||||||
`scripts/test_dependency_invalidation.sh`. The latter changes a transitive
|
`scripts/test_dependency_invalidation.sh`. The latter changes a transitive
|
||||||
`#load`, then replaces a warmed worker artifact while preserving its
|
`#load`, then replaces a warmed worker artifact while preserving its
|
||||||
whole-second mtime to prove both compiler and worker caches invalidate it. It
|
whole-second mtime to prove both compiler and worker caches invalidate it. It
|
||||||
also sends 48 requests and asserts the observed worker PID set does not exceed
|
also rejects an unreadable unit without publishing a wasm artifact, restores
|
||||||
|
its permissions and proves the next CLI request compiles it, then sends 48
|
||||||
|
requests and asserts the observed worker PID set does not exceed
|
||||||
`WORKER_COUNT`, guarding against accidental reintroduction of request-count
|
`WORKER_COUNT`, guarding against accidental reintroduction of request-count
|
||||||
recycling.
|
recycling.
|
||||||
`scripts/test_cold_component_deadline.sh` separately compiles a deliberately
|
`scripts/test_cold_component_deadline.sh` separately compiles a deliberately
|
||||||
|
|||||||
@ -54,6 +54,23 @@ if [[ "$(http_marker)" != *"dependency-marker-a"* ]]; then
|
|||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
printf '%s\n' 'CLI(Request& context) { print("readable-source-marker"); }' >"$source_dir/unreadable.uce"
|
||||||
|
chmod 000 "$source_dir/unreadable.uce"
|
||||||
|
if unreadable_output=$(scripts/uce-cli "/$test_name/unreadable.uce" 2>&1); then
|
||||||
|
echo "unreadable source unexpectedly compiled: $unreadable_output" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
if [[ "$unreadable_output" != *"source file is not readable"* ]]; then
|
||||||
|
echo "unreadable source did not report its actual compile error: $unreadable_output" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
if [[ -e "$cache_dir/unreadable.uce.wasm" ]]; then
|
||||||
|
echo "unreadable source published a wasm artifact" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
chmod 644 "$source_dir/unreadable.uce"
|
||||||
|
assert_marker unreadable readable-source-marker
|
||||||
|
|
||||||
sed -i 's/dependency-marker-a/dependency-marker-b/' "$source_dir/child.uce"
|
sed -i 's/dependency-marker-a/dependency-marker-b/' "$source_dir/child.uce"
|
||||||
started_at=$(date +%s%N)
|
started_at=$(date +%s%N)
|
||||||
http_during_rebuild=$(http_marker)
|
http_during_rebuild=$(http_marker)
|
||||||
|
|||||||
@ -2,8 +2,10 @@
|
|||||||
#include "compiler-parser.h"
|
#include "compiler-parser.h"
|
||||||
#include "hash.h"
|
#include "hash.h"
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
#include <cerrno>
|
||||||
#include <cstdlib>
|
#include <cstdlib>
|
||||||
#include <cctype>
|
#include <cctype>
|
||||||
|
#include <cstring>
|
||||||
#include <filesystem>
|
#include <filesystem>
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
@ -50,6 +52,7 @@ struct UnitSourceSignatureEntry
|
|||||||
u64 modified_ns = 0;
|
u64 modified_ns = 0;
|
||||||
u64 changed_ns = 0;
|
u64 changed_ns = 0;
|
||||||
u64 size = 0;
|
u64 size = 0;
|
||||||
|
bool readable = false;
|
||||||
String content_hash;
|
String content_hash;
|
||||||
StringList loaded_paths;
|
StringList loaded_paths;
|
||||||
};
|
};
|
||||||
@ -128,6 +131,18 @@ StringList compiler_unit_load_paths(String file_name, String content)
|
|||||||
return(paths);
|
return(paths);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool compiler_source_readable(String file_name, const struct stat& info, int* read_error = 0)
|
||||||
|
{
|
||||||
|
int error = 0;
|
||||||
|
if((info.st_mode & (S_IRUSR | S_IRGRP | S_IROTH)) == 0)
|
||||||
|
error = EACCES;
|
||||||
|
else if(access(file_name.c_str(), R_OK) != 0)
|
||||||
|
error = errno;
|
||||||
|
if(read_error)
|
||||||
|
*read_error = error;
|
||||||
|
return(error == 0);
|
||||||
|
}
|
||||||
|
|
||||||
UnitSourceSignatureEntry compiler_unit_source_entry(String file_name)
|
UnitSourceSignatureEntry compiler_unit_source_entry(String file_name)
|
||||||
{
|
{
|
||||||
struct stat info;
|
struct stat info;
|
||||||
@ -137,10 +152,11 @@ UnitSourceSignatureEntry compiler_unit_source_entry(String file_name)
|
|||||||
entry.modified_ns = (u64)info.st_mtim.tv_sec * 1000000000ull + (u64)info.st_mtim.tv_nsec;
|
entry.modified_ns = (u64)info.st_mtim.tv_sec * 1000000000ull + (u64)info.st_mtim.tv_nsec;
|
||||||
entry.changed_ns = (u64)info.st_ctim.tv_sec * 1000000000ull + (u64)info.st_ctim.tv_nsec;
|
entry.changed_ns = (u64)info.st_ctim.tv_sec * 1000000000ull + (u64)info.st_ctim.tv_nsec;
|
||||||
entry.size = (u64)info.st_size;
|
entry.size = (u64)info.st_size;
|
||||||
|
entry.readable = compiler_source_readable(file_name, info);
|
||||||
{
|
{
|
||||||
std::lock_guard<std::mutex> lock(unit_source_signature_cache_mutex);
|
std::lock_guard<std::mutex> lock(unit_source_signature_cache_mutex);
|
||||||
auto cached = unit_source_signature_cache.find(file_name);
|
auto cached = unit_source_signature_cache.find(file_name);
|
||||||
if(cached != unit_source_signature_cache.end() && cached->second.modified_ns == entry.modified_ns && cached->second.changed_ns == entry.changed_ns && cached->second.size == entry.size)
|
if(cached != unit_source_signature_cache.end() && cached->second.modified_ns == entry.modified_ns && cached->second.changed_ns == entry.changed_ns && cached->second.size == entry.size && cached->second.readable == entry.readable)
|
||||||
return(cached->second);
|
return(cached->second);
|
||||||
}
|
}
|
||||||
String content = file_get_contents(file_name);
|
String content = file_get_contents(file_name);
|
||||||
@ -165,7 +181,7 @@ void compiler_append_unit_source_signature(String file_name, std::set<String>& v
|
|||||||
visited.insert(normalized);
|
visited.insert(normalized);
|
||||||
|
|
||||||
UnitSourceSignatureEntry entry = compiler_unit_source_entry(normalized);
|
UnitSourceSignatureEntry entry = compiler_unit_source_entry(normalized);
|
||||||
signature += normalized + ":" + entry.content_hash + "\n";
|
signature += normalized + ":" + entry.content_hash + (entry.readable ? String("") : String(":unreadable")) + "\n";
|
||||||
for(String loaded : entry.loaded_paths)
|
for(String loaded : entry.loaded_paths)
|
||||||
compiler_append_unit_source_signature(loaded, visited, signature);
|
compiler_append_unit_source_signature(loaded, visited, signature);
|
||||||
}
|
}
|
||||||
@ -793,6 +809,22 @@ void compile_shared_unit(Request* context, SharedUnit* su)
|
|||||||
compiler_record_compile_result(su, time_precise() - comp_start, false, "missing_source", su->compiler_messages);
|
compiler_record_compile_result(su, time_precise() - comp_start, false, "missing_source", su->compiler_messages);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
struct stat source_info;
|
||||||
|
int read_error = 0;
|
||||||
|
bool source_readable = stat(su->file_name.c_str(), &source_info) == 0 && compiler_source_readable(su->file_name, source_info, &read_error);
|
||||||
|
if(!source_readable && read_error == 0)
|
||||||
|
read_error = errno == 0 ? ENOENT : errno;
|
||||||
|
if(!source_readable)
|
||||||
|
{
|
||||||
|
su->compiler_messages = "source file is not readable (" + su->file_name + "): " + std::strerror(read_error);
|
||||||
|
file_put_contents(su->compile_output_file_name, su->compiler_messages + "\n");
|
||||||
|
file_put_contents(su->wasm_check_file_name, su->compiler_messages + "\n");
|
||||||
|
file_put_contents(su->meta_file_name, compiler_unit_metadata_text(context, su));
|
||||||
|
file_unlink(su->wasm_name);
|
||||||
|
compiler_record_compile_result(su, time_precise() - comp_start, false, "compile_error", su->compiler_messages);
|
||||||
|
printf("%s \n", compiler_format_compile_failure(su, su->compiler_messages).c_str());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
shell_exec("mkdir -p " + shell_escape(su->pre_path));
|
shell_exec("mkdir -p " + shell_escape(su->pre_path));
|
||||||
file_put_contents(su->pre_path + "/" + su->pre_file_name, preprocess_shared_unit(context, su));
|
file_put_contents(su->pre_path + "/" + su->pre_file_name, preprocess_shared_unit(context, su));
|
||||||
|
|||||||
@ -438,8 +438,9 @@ int handle_cli_complete(FastCGIRequest& request)
|
|||||||
// W7e: compile a cold/stale unit on demand; native execution has
|
// W7e: compile a cold/stale unit on demand; native execution has
|
||||||
// been removed, so a unit that still cannot be served by wasm is a
|
// been removed, so a unit that still cannot be served by wasm is a
|
||||||
// request failure instead of a fallback path.
|
// request failure instead of a fallback path.
|
||||||
|
SharedUnit* cli_compile_state = 0;
|
||||||
if(!wasm_backend_should_handle(request, cli_unit))
|
if(!wasm_backend_should_handle(request, cli_unit))
|
||||||
get_shared_unit(&request, cli_unit);
|
cli_compile_state = get_shared_unit(&request, cli_unit);
|
||||||
if(wasm_backend_should_handle(request, cli_unit))
|
if(wasm_backend_should_handle(request, cli_unit))
|
||||||
{
|
{
|
||||||
String wasm_error = wasm_backend_serve(request, cli_unit, "cli");
|
String wasm_error = wasm_backend_serve(request, cli_unit, "cli");
|
||||||
@ -452,7 +453,11 @@ int handle_cli_complete(FastCGIRequest& request)
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
request.set_status(500, "Internal Server Error");
|
request.set_status(500, "Internal Server Error");
|
||||||
print("UCE CLI wasm unit unavailable after compile: ", script_filename, "\n");
|
String compile_error = cli_compile_state ? first(cli_compile_state->compiler_messages, cli_compile_state->compile_error_status) : String("");
|
||||||
|
print("UCE CLI wasm unit unavailable after compile: ", script_filename);
|
||||||
|
if(compile_error != "")
|
||||||
|
print(": ", compile_error);
|
||||||
|
print("\n");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -575,14 +580,16 @@ int handle_complete(FastCGIRequest& request) {
|
|||||||
// unit on demand — get_shared_unit() builds the .wasm side-module — and
|
// unit on demand — get_shared_unit() builds the .wasm side-module — and
|
||||||
// recheck. Native execution has been removed, so a unit that still cannot
|
// recheck. Native execution has been removed, so a unit that still cannot
|
||||||
// be served by wasm becomes a clean 500 request failure.
|
// be served by wasm becomes a clean 500 request failure.
|
||||||
|
SharedUnit* entry_compile_state = 0;
|
||||||
auto wasm_ready = [&](const String& unit) -> bool {
|
auto wasm_ready = [&](const String& unit) -> bool {
|
||||||
if(!wasm_backend_should_handle(request, unit))
|
if(!wasm_backend_should_handle(request, unit))
|
||||||
get_shared_unit(&request, unit);
|
entry_compile_state = get_shared_unit(&request, unit);
|
||||||
return(wasm_backend_should_handle(request, unit));
|
return(wasm_backend_should_handle(request, unit));
|
||||||
};
|
};
|
||||||
auto fail_wasm_unavailable = [&](const String& handler) {
|
auto fail_wasm_unavailable = [&](const String& handler) {
|
||||||
failure_title = "wasm unit unavailable after compile";
|
failure_title = "wasm unit unavailable after compile";
|
||||||
failure_details = handler + " handler could not be served by wasm";
|
String compile_error = entry_compile_state ? first(entry_compile_state->compiler_messages, entry_compile_state->compile_error_status) : String("");
|
||||||
|
failure_details = compile_error != "" ? compile_error : handler + " handler could not be served by wasm";
|
||||||
failure_trace = "source: " + request.params["SCRIPT_FILENAME"];
|
failure_trace = "source: " + request.params["SCRIPT_FILENAME"];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user