diff --git a/tests/Makefile b/tests/Makefile index 152444a..dfa1724 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -11,7 +11,7 @@ MBEDTLS_DIR := ../third_party/mbedtls/library MBEDTLS_SRCS := $(filter-out $(MBEDTLS_DIR)/net_sockets.c $(MBEDTLS_DIR)/timing.c $(MBEDTLS_DIR)/entropy_poll.c,$(wildcard $(MBEDTLS_DIR)/*.c)) ORBIS_SRCS := ../orbisrpc/jsonlite.c ../orbisrpc/b64.c ../orbisrpc/sfo.c ../orbisrpc/tmdb_crypto.c ../orbisrpc/updater_util.c ../orbisrpc/art.c ../orbisrpc/log.c ../orbisrpc/health.c ../orbisrpc/compat.c ../orbisrpc/cfg.c ../orbisrpc/appdb.c ../orbisrpc/discord.c ../orbisrpc/procwalk.c ../orbisrpc/bigapp.c ../orbisrpc/pkgzone.c ../orbisrpc/gamecache.c ../orbisrpc/fw.c ../orbisrpc/notify.c ../orbisrpc/retro.c ../orbisrpc/custom.c ../orbisrpc/jsonstr.c ../orbisrpc/timesync.c ../orbisrpc/supervise.c -INST_SRCS := ../installer/icfg.c +INST_SRCS := ../installer/icfg.c ../installer/jsonlite.c # SQLite amalgamation: -O0 for host iteration speed (payload uses -O2). SQLITE_DIR := ../third_party/sqlite/sqlite-amalgamation-3510100 SQLITE_FLAGS := -O2 -I$(SQLITE_DIR) # clean default compile; OMIT/THREADSAFE=0 overrides break sqlite3_open_v2 on the ubuntu runner @@ -19,25 +19,25 @@ SQLITE_FLAGS := -O2 -I$(SQLITE_DIR) # clean default compile; OMIT/THREADSAFE=0 o ORBIS_OBJS := $(patsubst ../orbisrpc/%.c,$(OBJDIR)/orbis_%.o,$(ORBIS_SRCS)) INST_OBJS := $(patsubst ../installer/%.c,$(OBJDIR)/inst_%.o,$(INST_SRCS)) MBEDTLS_OBJS := $(patsubst $(MBEDTLS_DIR)/%.c,$(OBJDIR)/mbed_%.o,$(MBEDTLS_SRCS)) -TEST_OBJS := $(OBJDIR)/test_utils.o $(ORBIS_OBJS) $(INST_OBJS) $(MBEDTLS_OBJS) $(OBJDIR)/sqlite3.o +TEST_OBJS := $(OBJDIR)/test_utils.o $(ORBIS_OBJS) $(MBEDTLS_OBJS) $(OBJDIR)/sqlite3.o ASAN_FLAGS := -fsanitize=address,undefined -fno-omit-frame-pointer ASAN_ORBIS_OBJS := $(patsubst ../orbisrpc/%.c,$(ASAN_OBJDIR)/orbis_%.o,$(ORBIS_SRCS)) ASAN_INST_OBJS := $(patsubst ../installer/%.c,$(ASAN_OBJDIR)/inst_%.o,$(INST_SRCS)) ASAN_MBEDTLS_OBJS := $(patsubst $(MBEDTLS_DIR)/%.c,$(ASAN_OBJDIR)/mbed_%.o,$(MBEDTLS_SRCS)) -ASAN_OBJS := $(ASAN_OBJDIR)/test_utils.o $(ASAN_ORBIS_OBJS) $(ASAN_INST_OBJS) $(ASAN_MBEDTLS_OBJS) $(ASAN_OBJDIR)/sqlite3.o +ASAN_OBJS := $(ASAN_OBJDIR)/test_utils.o $(ASAN_ORBIS_OBJS) $(ASAN_MBEDTLS_OBJS) $(ASAN_OBJDIR)/sqlite3.o .PHONY: all clean test asan all: $(TARGET) -test: $(TARGET) +test: $(TARGET) test_icfg ./$(TARGET) $(TARGET): $(TEST_OBJS) $(CC) $(CFLAGS) -o $@ $(TEST_OBJS) -lm -asan: $(ASAN_OBJS) +asan: $(ASAN_OBJS) test_icfg_asan $(CC) $(CFLAGS) $(ASAN_FLAGS) -o test_utils_asan $(ASAN_OBJS) -lm ./test_utils_asan @@ -87,5 +87,19 @@ $(ASAN_OBJDIR)/inst_%.o: ../installer/%.c | $(ASAN_OBJDIR) $(ASAN_OBJDIR)/mbed_%.o: $(MBEDTLS_DIR)/%.c | $(ASAN_OBJDIR) $(CC) $(CFLAGS) $(ASAN_FLAGS) -Wno-unused-parameter -c -o $@ $< +$(OBJDIR)/test_icfg.o: test_icfg.c | $(OBJDIR) + $(CC) $(CFLAGS) -I../installer -c -o $@ test_icfg.c + +$(ASAN_OBJDIR)/test_icfg.o: test_icfg.c | $(ASAN_OBJDIR) + $(CC) $(CFLAGS) $(ASAN_FLAGS) -I../installer -c -o $@ test_icfg.c + +test_icfg: $(OBJDIR)/test_icfg.o $(INST_OBJS) + $(CC) $(CFLAGS) -o $@ $(OBJDIR)/test_icfg.o $(INST_OBJS) -lm + ./test_icfg + +test_icfg_asan: $(ASAN_OBJDIR)/test_icfg.o $(ASAN_INST_OBJS) + $(CC) $(CFLAGS) $(ASAN_FLAGS) -o $@ $(ASAN_OBJDIR)/test_icfg.o $(ASAN_INST_OBJS) -lm + ./test_icfg_asan + clean: - rm -rf $(TARGET) test_utils_asan $(OBJDIR) $(ASAN_OBJDIR) *.o + rm -rf $(TARGET) test_utils_asan test_icfg test_icfg_asan $(OBJDIR) $(ASAN_OBJDIR) *.o diff --git a/tests/test_icfg.c b/tests/test_icfg.c new file mode 100644 index 0000000..7ec636a --- /dev/null +++ b/tests/test_icfg.c @@ -0,0 +1,77 @@ +/* test_icfg.c - installer config tests against installer/jsonlite. + * + * icfg.c includes installer/jsonlite.h (quoted include resolves to its own + * directory), whose jl_val layout and enum values differ from the payload's + * orbis/jsonlite. Linking icfg.o against orbis jsonlite.o therefore + * misreads every value (e.g. JL_OBJECT is 5 here, 4 there). The real + * installer build links its own C sources together, so this binary mirrors + * that: icfg.o + installer jsonlite.o, nothing from orbis/. + */ +#include +#include +#include +#include +#include +#include +#include "../installer/icfg.h" + +static int make_tmpdir(char *out, size_t cap){ + static int seq = 0; + snprintf(out, cap, "/tmp/orx_icfg_%d_%d", (int)getpid(), seq++); + if(mkdir(out, 0700) != 0) return -1; + return 0; +} + +static void test_installer_cfg(void) { + char dir[64], path[96], path2[96]; + assert(make_tmpdir(dir, sizeof dir) == 0); + snprintf(path, sizeof path, "%s/config.json", dir); + /* token validation */ + assert(token_valid("MTIz.ABCdef_0123456789-xYz.ABCDEFGHijklmnopQRSTUVwxyz") == 1); + assert(token_valid("short") == 0); + assert(token_valid("SET_ME") == 0); + assert(token_valid("has space.in.it.aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa") == 0); + assert(token_valid("nodots") == 0); + assert(token_valid(NULL) == 0); + /* fixture via stdio: no C write API exists anymore (the C++ settings + * page owns writes); these tests pin the read + carry API it relies on */ + FILE *f = fopen(path, "wb"); + assert(f); + fputs("{\"token\":\"MTIz.ABCdef_0123456789-xYz.ABCDEFGHijklmnopQRSTUVwxyz\"," + "\"presence_state\":\"On PS4\",\"poll_interval_s\":12}", f); + fclose(f); + char tok[160], st[64]; + long n = 0; + assert(icfg_token_load(path, tok, sizeof tok) == 0); + assert(!strcmp(tok, "MTIz.ABCdef_0123456789-xYz.ABCDEFGHijklmnopQRSTUVwxyz")); + assert(icfg_get_str(path, "presence_state", st, sizeof st) == 0); + assert(!strcmp(st, "On PS4")); + assert(icfg_get_int(path, "poll_interval_s", &n) == 0 && n == 12); + /* missing file: loads fail soft */ + assert(icfg_get_str("/nonexistent/x.json", "k", st, sizeof st) != 0); + /* carry round-trip across a wholesale overwrite */ + const char *keys[] = { "token", "presence_state", "poll_interval_s", "nope_missing", NULL }; + icfg_carry_t vals[4]; + assert(icfg_carry_capture(path, keys, vals, 4) == 3); + snprintf(path2, sizeof path2, "%s/config2.json", dir); + FILE *g = fopen(path2, "wb"); + assert(g); + fputs("{\"schema_version\":1}", g); + fclose(g); + assert(icfg_carry_apply(path2, vals, 4) == 3); + assert(icfg_token_load(path2, tok, sizeof tok) == 0); + assert(!strcmp(tok, "MTIz.ABCdef_0123456789-xYz.ABCDEFGHijklmnopQRSTUVwxyz")); + assert(icfg_get_int(path2, "poll_interval_s", &n) == 0 && n == 12); + /* corrupt source: capture yields all-absent, never garbage */ + FILE *h = fopen(path, "wb"); + assert(h); + fputs("{not json", h); + fclose(h); + assert(icfg_carry_capture(path, keys, vals, 4) == 0); +} + +int main(void){ + test_installer_cfg(); + puts("icfg tests passed"); + return 0; +} diff --git a/tests/test_utils.c b/tests/test_utils.c index f2d354d..6fc18d1 100644 --- a/tests/test_utils.c +++ b/tests/test_utils.c @@ -1045,34 +1045,6 @@ static void test_cfg_learn(void) { assert(cfg_learn(&c, "CUSA00001", "Full Game") == 0); } -static void test_installer_cfg(void) { - char dir[64], path[96]; - assert(make_tmpdir(dir, sizeof dir) == 0); - snprintf(path, sizeof path, "%s/config.json", dir); - /* token validation */ - assert(token_valid("MTIz.ABCdef_0123456789-xYz.ABCDEFGHijklmnopQRSTUVwxyz") == 1); - assert(token_valid("short") == 0); - assert(token_valid("SET_ME") == 0); - assert(token_valid("has space.in.it.aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa") == 0); - assert(token_valid("nodots") == 0); - assert(token_valid(NULL) == 0); - /* save creates, preserves, round-trips */ - assert(icfg_token_save(path, "MTIz.ABCdef_0123456789-xYz.ABCDEFGHijklmnopQRSTUVwxyz") == 0); - assert(icfg_set_str(path, "presence_state", "On PS4") == 0); - assert(icfg_set_int(path, "poll_interval_s", 12) == 0); - /* invalid token refused, file untouched */ - assert(icfg_token_save(path, "junk") == -1); - char tok[160], st[64]; - long n = 0; - assert(icfg_token_load(path, tok, sizeof tok) == 0); - assert(!strcmp(tok, "MTIz.ABCdef_0123456789-xYz.ABCDEFGHijklmnopQRSTUVwxyz")); - assert(icfg_get_str(path, "presence_state", st, sizeof st) == 0); - assert(!strcmp(st, "On PS4")); - assert(icfg_get_int(path, "poll_interval_s", &n) == 0 && n == 12); - /* missing file: loads fail soft, save still works */ - assert(icfg_get_str("/nonexistent/x.json", "k", st, sizeof st) != 0); -} - /* --- procwalk: process-table record parsing --------------------------- * detect.c used to hard-code "eboot.bin" at byte 447 and reject records * under 479 bytes. Those are 9.00 numbers; on a firmware that shifts @@ -1874,7 +1846,6 @@ int main(void) { test_health_safe_mode(); test_cfg_titles(); test_cfg_learn(); - test_installer_cfg(); test_appdb(); test_discord_builder(); test_time_apply_unsynced();