From b937d7b31a715678c71719ee189be0c1c1992a33 Mon Sep 17 00:00:00 2001 From: zqz Date: Fri, 14 Aug 2026 10:08:47 +0800 Subject: [PATCH 1/4] LATX, fix: Validate AOT cache footers exactly AOT readers copied a fixed-width footer into a non-NUL-terminated buffer and passed it to strstr(). That can read beyond the footer and make cache acceptance depend on unrelated memory. Centralize footer validation in the AOT file helper and compare the exact stored byte span before either counting or loading an AOT cache. Close a rejected counting reader so the new shared validation preserves the previous stream ownership. Signed-off-by: zqz --- target/i386/latx/include/file_ctx.h | 1 + target/i386/latx/sbt/aot.c | 21 +++------------------ target/i386/latx/sbt/file_ctx.c | 22 ++++++++++++++++++++++ 3 files changed, 26 insertions(+), 18 deletions(-) diff --git a/target/i386/latx/include/file_ctx.h b/target/i386/latx/include/file_ctx.h index 4e0c5e7346d..330afa6aca2 100644 --- a/target/i386/latx/include/file_ctx.h +++ b/target/i386/latx/include/file_ctx.h @@ -17,6 +17,7 @@ int aot_file_get_tmp_path(const char *aot_file, char *tmp_path, size_t tmp_path_size); int aot_file_get_lock_path(const char *aot_file, char *lock_path, size_t lock_path_size); +bool aot_file_has_footer(FILE *file, const char *footer); int aot_file_complete_write(FILE *file, const char *tmp_path); /* * Negative: rename failed. Zero: rename and directory sync succeeded. diff --git a/target/i386/latx/sbt/aot.c b/target/i386/latx/sbt/aot.c index 91d6c19e14d..4a0e42c6b0a 100644 --- a/target/i386/latx/sbt/aot.c +++ b/target/i386/latx/sbt/aot.c @@ -902,21 +902,11 @@ static int get_tb_num(char *lib_name, char *aot_file_name, CPUState *cpu) /* Get file size */ fseek(pf, 0, SEEK_END); /* seek to end of file */ size_t file_sz = ftell(pf); /* get current file pointer */ - char aot_version[strlen(AOT_VERSION) + 1]; /*check aot complete.*/ - if (fseek(pf, -strlen(AOT_VERSION), SEEK_END) != 0) { - qemu_log_mask(LAT_LOG_AOT, "can't fseek aot file\n"); - fclose(pf); - return 0; - } - if (fread(aot_version, strlen(AOT_VERSION), 1, pf) != 1) { - qemu_log_mask(LAT_LOG_AOT, "get error.\n"); - fclose(pf); - return 0; - } - if (!strstr(aot_version, AOT_VERSION)) { + if (!aot_file_has_footer(pf, AOT_VERSION)) { qemu_log_mask(LAT_LOG_AOT, "aot file is not complete %s\n", lib_name); remove(aot_file_path); + fclose(pf); return 0; } fseek(pf, 0, SEEK_SET); /* seek back to beginning of file */ @@ -1305,7 +1295,6 @@ lib_info *aot_load(char *lib_name, char *aot_file_name, struct stat statbuf; lib_info *curr_lib_info = NULL; size_t file_sz = 0; - char aot_version[strlen(AOT_VERSION) + 1]; int fd = open(aot_file_path, O_RDONLY); FILE *pf = NULL; if (fd < 0) { @@ -1317,11 +1306,7 @@ lib_info *aot_load(char *lib_name, char *aot_file_name, file_sz = ftell(pf); /* get current file pointer */ /*check aot complete.*/ - if (fseek(pf, -strlen(AOT_VERSION), SEEK_END) != 0 - || fread(aot_version, strlen(AOT_VERSION), 1, pf) != 1) { - goto exit_aot_load; - } - if (!strstr(aot_version, AOT_VERSION)) { + if (!aot_file_has_footer(pf, AOT_VERSION)) { qemu_log_mask(LAT_LOG_AOT, "aot file is not complete %s\n", lib_name); remove_curr_aot_file(fd); goto exit_aot_load; diff --git a/target/i386/latx/sbt/file_ctx.c b/target/i386/latx/sbt/file_ctx.c index 7a765a7945e..6e316f3cfd6 100644 --- a/target/i386/latx/sbt/file_ctx.c +++ b/target/i386/latx/sbt/file_ctx.c @@ -49,6 +49,28 @@ int aot_file_get_lock_path(const char *aot_file, char *lock_path, return 0; } +bool aot_file_has_footer(FILE *file, const char *footer) +{ + size_t footer_len; + char *actual; + bool matches; + + if (!file || !footer) { + return false; + } + + footer_len = strlen(footer); + if (!footer_len || fseek(file, -(long)footer_len, SEEK_END) != 0) { + return false; + } + + actual = g_malloc(footer_len); + matches = fread(actual, footer_len, 1, file) == 1 && + !memcmp(actual, footer, footer_len); + g_free(actual); + return matches; +} + int aot_file_complete_write(FILE *file, const char *tmp_path) { int saved_errno = 0; From 54417f917b810dd9266dbd3509aa18f2cd771339 Mon Sep 17 00:00:00 2001 From: zqz Date: Fri, 14 Aug 2026 10:09:54 +0800 Subject: [PATCH 2/4] LATX, tests: Cover exact AOT cache footer validation Exercise the shared AOT footer validator with an exact footer, an equal-length byte mismatch, and a requested footer longer than the file. The coverage stays in the existing file-context test target and does not require a guest runtime or a system cache. Signed-off-by: zqz --- target/i386/latx/sbt/tests/aot-file-publish-test.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/target/i386/latx/sbt/tests/aot-file-publish-test.c b/target/i386/latx/sbt/tests/aot-file-publish-test.c index 4bea1bcf795..b304e49f517 100644 --- a/target/i386/latx/sbt/tests/aot-file-publish-test.c +++ b/target/i386/latx/sbt/tests/aot-file-publish-test.c @@ -90,6 +90,15 @@ int main(void) g_assert(aot_file_get_tmp_path(aot_path, tmp_path, sizeof(tmp_path)) == 0); + file = fopen(tmp_path, "w+"); + g_assert(file != NULL); + g_assert(fputs("payload:Version: current", file) >= 0); + g_assert(aot_file_has_footer(file, "Version: current")); + g_assert(!aot_file_has_footer(file, "Version: currenT")); + g_assert(!aot_file_has_footer(file, "payload:Version: current+extra")); + g_assert(fclose(file) == 0); + g_assert(unlink(tmp_path) == 0); + g_assert(g_file_set_contents(aot_path, "old", -1, NULL)); file = fopen(tmp_path, "w"); g_assert(file != NULL); From d61fed40f53a5e7300af2969e5c407fbf0e4b425 Mon Sep 17 00:00:00 2001 From: zqz Date: Fri, 14 Aug 2026 11:33:21 +0800 Subject: [PATCH 3/4] LATX, fix: Clean up rejected AOT cache readers Validate cache file size before reading an AOT header and route every get_tb_num() reader exit through one mapping and stream cleanup path. Also handle open and fdopen failures in both readers, use MAP_FAILED for mmap failure checks, and avoid closing the descriptor again after fclose() in aot_load(). Signed-off-by: zqz --- target/i386/latx/sbt/aot.c | 72 +++++++++++++++++++++++++++++--------- 1 file changed, 55 insertions(+), 17 deletions(-) diff --git a/target/i386/latx/sbt/aot.c b/target/i386/latx/sbt/aot.c index 4a0e42c6b0a..7a61d833674 100644 --- a/target/i386/latx/sbt/aot.c +++ b/target/i386/latx/sbt/aot.c @@ -890,33 +890,54 @@ char in_white_list(char *lib) static int get_tb_num(char *lib_name, char *aot_file_name, CPUState *cpu) { + void *buffer = MAP_FAILED; + FILE *pf = NULL; + long file_size; + size_t file_sz = 0; + int aim_tb_num = 0; + int fd; + if (get_aot_path(aot_file_name, aot_file_path, PATH_MAX) < 0) { return 0; } if (access(aot_file_path, 0) < 0) { return 0; } - int fd = open(aot_file_path, O_RDONLY); - FILE *pf = fdopen(fd, "r"); - lsassert(pf && ("open aot file failed!")); + fd = open(aot_file_path, O_RDONLY); + if (fd < 0) { + return 0; + } + pf = fdopen(fd, "r"); + if (!pf) { + close(fd); + return 0; + } + /* Get file size */ - fseek(pf, 0, SEEK_END); /* seek to end of file */ - size_t file_sz = ftell(pf); /* get current file pointer */ + if (fseek(pf, 0, SEEK_END) || (file_size = ftell(pf)) < 0) { + goto out; + } + file_sz = file_size; + if (file_sz < sizeof(aot_header)) { + qemu_log_mask(LAT_LOG_AOT, "aot file is too short %s\n", lib_name); + remove(aot_file_path); + goto out; + } + /*check aot complete.*/ if (!aot_file_has_footer(pf, AOT_VERSION)) { qemu_log_mask(LAT_LOG_AOT, "aot file is not complete %s\n", lib_name); remove(aot_file_path); - fclose(pf); - return 0; + goto out; } fseek(pf, 0, SEEK_SET); /* seek back to beginning of file */ /* Read aot file */ /* buffer = malloc(file_sz); */ - void *buffer = mmap(NULL, file_sz, PROT_READ, MAP_SHARED, fd, 0); - if (buffer < 0) { + buffer = mmap(NULL, file_sz, PROT_READ, MAP_SHARED, fd, 0); + if (buffer == MAP_FAILED) { qemu_log_mask(LAT_LOG_AOT, "aot file mmap error\n"); - return 0; + goto out; } assert(buffer); aot_header *p_header = (aot_header *)buffer; @@ -931,17 +952,19 @@ static int get_tb_num(char *lib_name, char *aot_file_name, CPUState *cpu) aot_file_path, p_header->lib_size, statbuf.st_size); remove(aot_file_path); qemu_log_mask(LAT_LOG_AOT, "remove end\n"); - return 0; + goto out; } - int aim_tb_num = 0; if (cpu->tcg_cflags & CF_PARALLEL) { aim_tb_num = p_header->parallel_tb_num; } else { aim_tb_num = p_header->unparallel_tb_num; } - munmap(buffer, file_sz); +out: + if (buffer != MAP_FAILED) { + munmap(buffer, file_sz); + } fclose(pf); return aim_tb_num; } @@ -1295,15 +1318,29 @@ lib_info *aot_load(char *lib_name, char *aot_file_name, struct stat statbuf; lib_info *curr_lib_info = NULL; size_t file_sz = 0; + long file_size; int fd = open(aot_file_path, O_RDONLY); FILE *pf = NULL; + if (fd < 0) { goto exit_aot_load; } pf = fdopen(fd, "r"); - lsassert(pf && ("open aot file failed!")); - fseek(pf, 0, SEEK_END); /* seek to end of file */ - file_sz = ftell(pf); /* get current file pointer */ + if (!pf) { + close(fd); + fd = -1; + goto exit_aot_load; + } + + if (fseek(pf, 0, SEEK_END) || (file_size = ftell(pf)) < 0) { + goto exit_aot_load; + } + if ((size_t)file_size < sizeof(aot_header)) { + qemu_log_mask(LAT_LOG_AOT, "aot file is too short %s\n", lib_name); + remove_curr_aot_file(fd); + goto exit_aot_load; + } + file_sz = file_size; /*check aot complete.*/ if (!aot_file_has_footer(pf, AOT_VERSION)) { @@ -1345,8 +1382,9 @@ lib_info *aot_load(char *lib_name, char *aot_file_name, } if (likely(pf)) { fclose(pf); + } else if (fd >= 0) { + close(fd); } - close(fd); return curr_lib_info; } From b36c54bb84be69d0f27782184f59d6cdb42f5bed Mon Sep 17 00:00:00 2001 From: zqz Date: Fri, 14 Aug 2026 11:38:02 +0800 Subject: [PATCH 4/4] LATX, tests: Cover AOT cache reader cleanup Exercise invalid-footer and truncated-header cache paths repeatedly, then verify successful loading closes its stream without closing the descriptor twice. Also inject an fdopen() failure to verify that aot_load() closes the original descriptor exactly once. Signed-off-by: zqz --- meson.build | 22 ++ target/i386/latx/include/aot_reader.h | 14 ++ target/i386/latx/include/file_ctx.h | 1 + target/i386/latx/sbt/aot.c | 213 +----------------- target/i386/latx/sbt/aot_reader.c | 184 +++++++++++++++ target/i386/latx/sbt/file_ctx.c | 30 +++ target/i386/latx/sbt/meson.build | 1 + .../latx/sbt/tests/aot-cache-reader-test.c | 200 ++++++++++++++++ 8 files changed, 454 insertions(+), 211 deletions(-) create mode 100644 target/i386/latx/include/aot_reader.h create mode 100644 target/i386/latx/sbt/aot_reader.c create mode 100644 target/i386/latx/sbt/tests/aot-cache-reader-test.c diff --git a/meson.build b/meson.build index 632b1c90c66..86374378738 100644 --- a/meson.build +++ b/meson.build @@ -696,6 +696,28 @@ foreach target : target_dirs suite: 'lat-pr-fast', ) + aot_cache_reader_test = executable( + 'test-latx-aot-cache-reader', + files( + 'target/i386/latx/sbt/tests/aot-cache-reader-test.c', + 'target/i386/latx/sbt/aot_reader.c', + 'target/i386/latx/sbt/aot_lib.c', + 'target/i386/latx/sbt/file_ctx.c' + ) + genh + tcg_trace_genh + [syscall_nr_generated], + c_args: smc_reload_test_c_args, + dependencies: [glib], + include_directories: target_inc, + link_args: smc_reload_test_link_args + [ + '-Wl,--wrap=fdopen', + '-Wl,--wrap=fclose' + ] + ) + test( + 'latx-aot-cache-reader', + aot_cache_reader_test, + suite: 'lat-pr-fast', + ) + aot_exit_worker_test = executable( 'test-latx-aot-exit-worker', files( diff --git a/target/i386/latx/include/aot_reader.h b/target/i386/latx/include/aot_reader.h new file mode 100644 index 00000000000..a55ba243d18 --- /dev/null +++ b/target/i386/latx/include/aot_reader.h @@ -0,0 +1,14 @@ +/* + * SPDX-FileCopyrightText: 2021-2026 LAT Project Authors + * + * SPDX-License-Identifier: GPL-2.0-only + */ + +#ifndef LATX_AOT_READER_H +#define LATX_AOT_READER_H + +#include "aot.h" + +int aot_get_tb_num(char *lib_name, char *aot_file_name, CPUState *cpu); + +#endif diff --git a/target/i386/latx/include/file_ctx.h b/target/i386/latx/include/file_ctx.h index 330afa6aca2..554fa4adf4b 100644 --- a/target/i386/latx/include/file_ctx.h +++ b/target/i386/latx/include/file_ctx.h @@ -12,6 +12,7 @@ #ifndef AOT_FILE_CTX_H #define AOT_FILE_CTX_H #include "aot.h" + int aot_file_ctx(uint64_t maxSize, uint64_t leftMinSize); int aot_file_get_tmp_path(const char *aot_file, char *tmp_path, size_t tmp_path_size); diff --git a/target/i386/latx/sbt/aot.c b/target/i386/latx/sbt/aot.c index 7a61d833674..3aa6292cfac 100644 --- a/target/i386/latx/sbt/aot.c +++ b/target/i386/latx/sbt/aot.c @@ -22,6 +22,7 @@ #include "qemu.h" #include "lsenv.h" #include "file_ctx.h" +#include "aot_reader.h" #include "aot_merge.h" #include "aot_recover_tb.h" #include "aot_smc.h" @@ -657,36 +658,6 @@ static uint8_t *fill_ir1_buff(uint64_t ir1_table_offset, aot_tb *p_aot_tbs, return ir1_buffer; } -int get_aot_path(const char *lib_name, char *file_path, - size_t file_path_size) -{ - const char *home; - char *escaped_name; - int len; - - if (!lib_name || !file_path || !file_path_size) { - return -EINVAL; - } - escaped_name = g_strdup(lib_name); - if (!escaped_name) { - return -ENOMEM; - } - for (char *p = escaped_name; *p; p++) { - if (*p == '/') { - *p = '+'; - } - } - home = getenv("HOME"); - len = snprintf(file_path, file_path_size, "%s/.cache/latx/%s.aot2", - home ? home : "", escaped_name); - g_free(escaped_name); - if (len < 0 || (size_t)len >= file_path_size) { - file_path[0] = '\0'; - return -ENAMETOOLONG; - } - return 0; -} - static long get_fileSize(FILE* file) { long fileSize = -1; @@ -888,87 +859,6 @@ char in_white_list(char *lib) return in_list(lib, lib_white_list); } -static int get_tb_num(char *lib_name, char *aot_file_name, CPUState *cpu) -{ - void *buffer = MAP_FAILED; - FILE *pf = NULL; - long file_size; - size_t file_sz = 0; - int aim_tb_num = 0; - int fd; - - if (get_aot_path(aot_file_name, aot_file_path, PATH_MAX) < 0) { - return 0; - } - if (access(aot_file_path, 0) < 0) { - return 0; - } - fd = open(aot_file_path, O_RDONLY); - if (fd < 0) { - return 0; - } - pf = fdopen(fd, "r"); - if (!pf) { - close(fd); - return 0; - } - - /* Get file size */ - if (fseek(pf, 0, SEEK_END) || (file_size = ftell(pf)) < 0) { - goto out; - } - file_sz = file_size; - if (file_sz < sizeof(aot_header)) { - qemu_log_mask(LAT_LOG_AOT, "aot file is too short %s\n", lib_name); - remove(aot_file_path); - goto out; - } - - /*check aot complete.*/ - if (!aot_file_has_footer(pf, AOT_VERSION)) { - qemu_log_mask(LAT_LOG_AOT, "aot file is not complete %s\n", lib_name); - remove(aot_file_path); - goto out; - } - fseek(pf, 0, SEEK_SET); /* seek back to beginning of file */ - - /* Read aot file */ - /* buffer = malloc(file_sz); */ - buffer = mmap(NULL, file_sz, PROT_READ, MAP_SHARED, fd, 0); - if (buffer == MAP_FAILED) { - qemu_log_mask(LAT_LOG_AOT, "aot file mmap error\n"); - goto out; - } - assert(buffer); - aot_header *p_header = (aot_header *)buffer; - /* dump_aot_buffer(p_header); */ - struct stat statbuf; - if ((p_header->aot_file_type & (ELF_AOT_FILE | PE_AOT_FILE)) - && (stat(lib_name, &statbuf) - || p_header->lib_size != statbuf.st_size - || p_header->last_modify_time.tv_sec != statbuf.st_mtim.tv_sec - || p_header->last_modify_time.tv_nsec != statbuf.st_mtim.tv_nsec)) { - qemu_log_mask(LAT_LOG_AOT, "need remove old aot file. %s lib_size %d %ld\n", - aot_file_path, p_header->lib_size, statbuf.st_size); - remove(aot_file_path); - qemu_log_mask(LAT_LOG_AOT, "remove end\n"); - goto out; - } - - if (cpu->tcg_cflags & CF_PARALLEL) { - aim_tb_num = p_header->parallel_tb_num; - } else { - aim_tb_num = p_header->unparallel_tb_num; - } - -out: - if (buffer != MAP_FAILED) { - munmap(buffer, file_sz); - } - fclose(pf); - return aim_tb_num; -} - #define PROLIFERATION_RATE 10 static inline int need_gen_aot(CPUState *cpu, char *lib_name, char *aot_file_name, int *lockfd, uint32_t tb_count) @@ -983,7 +873,7 @@ static inline int need_gen_aot(CPUState *cpu, aim_tb_num = p_head->unparallel_tb_num; } } else { - aim_tb_num = get_tb_num(lib_name, aot_file_name, cpu); + aim_tb_num = aot_get_tb_num(lib_name, aot_file_name, cpu); } /* Too few tb number. */ if (tb_count * PROLIFERATION_RATE < aim_tb_num) { @@ -1288,106 +1178,8 @@ void dump_aot_buffer(aot_header *p_header) } -static void remove_curr_aot_file(int fd) -{ - char lock_path[PATH_MAX]; - - if (aot_file_get_lock_path(aot_file_path, lock_path, - sizeof(lock_path)) < 0) { - return; - } - aot_file_unlink_if_same(aot_file_path, fd, lock_path); -} - time_t aot_st_ctime; -lib_info *aot_load(char *lib_name, char *aot_file_name, - void **curr_aot_buffer) -{ - /* Get aot_file path and lock file. */ - assert(lib_name); - if (get_aot_path(aot_file_name, aot_file_path, PATH_MAX) < 0) { - return NULL; - } - if (access(aot_file_path, 0) < 0) { - return NULL; - } - - /* Open aot_file. */ - void *buffer = MAP_FAILED; - struct stat statbuf; - lib_info *curr_lib_info = NULL; - size_t file_sz = 0; - long file_size; - int fd = open(aot_file_path, O_RDONLY); - FILE *pf = NULL; - - if (fd < 0) { - goto exit_aot_load; - } - pf = fdopen(fd, "r"); - if (!pf) { - close(fd); - fd = -1; - goto exit_aot_load; - } - - if (fseek(pf, 0, SEEK_END) || (file_size = ftell(pf)) < 0) { - goto exit_aot_load; - } - if ((size_t)file_size < sizeof(aot_header)) { - qemu_log_mask(LAT_LOG_AOT, "aot file is too short %s\n", lib_name); - remove_curr_aot_file(fd); - goto exit_aot_load; - } - file_sz = file_size; - - /*check aot complete.*/ - if (!aot_file_has_footer(pf, AOT_VERSION)) { - qemu_log_mask(LAT_LOG_AOT, "aot file is not complete %s\n", lib_name); - remove_curr_aot_file(fd); - goto exit_aot_load; - } - - /* Read aot file */ - fseek(pf, 0, SEEK_SET); /* seek back to beginning of file */ - buffer = mmap(NULL, file_sz, PROT_READ, MAP_SHARED, fd, 0); - if (buffer == MAP_FAILED) { - qemu_log_mask(LAT_LOG_AOT, "aot file mmap error\n"); - goto exit_aot_load; - } - assert(buffer); - aot_header *p_header = (aot_header *)buffer; - - /* Test original file state. */ - if (p_header->aot_file_type & (ELF_AOT_FILE | PE_AOT_FILE)) { - if (stat(lib_name, &statbuf) - || p_header->lib_size != statbuf.st_size - || p_header->last_modify_time.tv_sec != statbuf.st_mtim.tv_sec - || p_header->last_modify_time.tv_nsec != statbuf.st_mtim.tv_nsec) { - qemu_log_mask(LAT_LOG_AOT, "need remove old aot file. %s lib_size %d %ld\n", - aot_file_path, p_header->lib_size, statbuf.st_size); - remove_curr_aot_file(fd); - goto exit_aot_load; - } - } - - /* dump_aot_buffer(p_header); */ - *curr_aot_buffer = buffer; - curr_lib_info = lib_tree_insert(aot_file_name, buffer, file_sz); - -exit_aot_load: - if (!curr_lib_info && buffer != MAP_FAILED) { - munmap(buffer, file_sz); - } - if (likely(pf)) { - fclose(pf); - } else if (fd >= 0) { - close(fd); - } - return curr_lib_info; -} - struct aot_segment *aot_find_segment(char *path, int offset, void *curr_aot_buffer) { struct aot_header *p_header = (struct aot_header *)curr_aot_buffer; @@ -1668,7 +1460,6 @@ void aot_do_tb_reloc(TranslationBlock *tb, struct aot_tb *stb, } } } - static aot_segment *get_segment(seg_info *seg, char *lib_name, uint64_t aot_offset, abi_long start, abi_long end, void **curr_aot_buffer) diff --git a/target/i386/latx/sbt/aot_reader.c b/target/i386/latx/sbt/aot_reader.c new file mode 100644 index 00000000000..2273bc84fb0 --- /dev/null +++ b/target/i386/latx/sbt/aot_reader.c @@ -0,0 +1,184 @@ +/* + * SPDX-FileCopyrightText: 2021-2026 LAT Project Authors + * + * SPDX-License-Identifier: GPL-2.0-only + */ + +#include "qemu-def.h" +#include "aot.h" +#include "aot_reader.h" +#include "file_ctx.h" +#include "qemu.h" + +#ifdef CONFIG_LATX_AOT + +int aot_get_tb_num(char *lib_name, char *aot_file_name, CPUState *cpu) +{ + void *buffer = MAP_FAILED; + FILE *pf = NULL; + long file_size; + size_t file_sz = 0; + int aim_tb_num = 0; + int fd; + + if (get_aot_path(aot_file_name, aot_file_path, PATH_MAX) < 0) { + return 0; + } + if (access(aot_file_path, 0) < 0) { + return 0; + } + fd = open(aot_file_path, O_RDONLY); + if (fd < 0) { + return 0; + } + pf = fdopen(fd, "r"); + if (!pf) { + close(fd); + return 0; + } + + /* Get file size */ + if (fseek(pf, 0, SEEK_END) || (file_size = ftell(pf)) < 0) { + goto out; + } + file_sz = file_size; + if (file_sz < sizeof(aot_header)) { + qemu_log_mask(LAT_LOG_AOT, "aot file is too short %s\n", lib_name); + remove(aot_file_path); + goto out; + } + + /* Check that the AOT file was completely written. */ + if (!aot_file_has_footer(pf, AOT_VERSION)) { + qemu_log_mask(LAT_LOG_AOT, "aot file is not complete %s\n", lib_name); + remove(aot_file_path); + goto out; + } + fseek(pf, 0, SEEK_SET); + + buffer = mmap(NULL, file_sz, PROT_READ, MAP_SHARED, fd, 0); + if (buffer == MAP_FAILED) { + qemu_log_mask(LAT_LOG_AOT, "aot file mmap error\n"); + goto out; + } + assert(buffer); + aot_header *p_header = (aot_header *)buffer; + struct stat statbuf; + + if ((p_header->aot_file_type & (ELF_AOT_FILE | PE_AOT_FILE)) + && (stat(lib_name, &statbuf) + || p_header->lib_size != statbuf.st_size + || p_header->last_modify_time.tv_sec != statbuf.st_mtim.tv_sec + || p_header->last_modify_time.tv_nsec != statbuf.st_mtim.tv_nsec)) { + qemu_log_mask(LAT_LOG_AOT, + "need remove old aot file. %s lib_size %d %ld\n", + aot_file_path, p_header->lib_size, statbuf.st_size); + remove(aot_file_path); + qemu_log_mask(LAT_LOG_AOT, "remove end\n"); + goto out; + } + + if (cpu->tcg_cflags & CF_PARALLEL) { + aim_tb_num = p_header->parallel_tb_num; + } else { + aim_tb_num = p_header->unparallel_tb_num; + } + +out: + if (buffer != MAP_FAILED) { + munmap(buffer, file_sz); + } + fclose(pf); + return aim_tb_num; +} + +static void remove_curr_aot_file(int fd) +{ + char lock_path[PATH_MAX]; + + if (aot_file_get_lock_path(aot_file_path, lock_path, + sizeof(lock_path)) < 0) { + return; + } + aot_file_unlink_if_same(aot_file_path, fd, lock_path); +} + +lib_info *aot_load(char *lib_name, char *aot_file_name, + void **curr_aot_buffer) +{ + void *buffer = MAP_FAILED; + struct stat statbuf; + lib_info *curr_lib_info = NULL; + size_t file_sz = 0; + long file_size; + int fd; + FILE *pf = NULL; + + assert(lib_name); + if (get_aot_path(aot_file_name, aot_file_path, PATH_MAX) < 0) { + return NULL; + } + if (access(aot_file_path, 0) < 0) { + return NULL; + } + + fd = open(aot_file_path, O_RDONLY); + if (fd < 0) { + return NULL; + } + pf = fdopen(fd, "r"); + if (!pf) { + close(fd); + return NULL; + } + + if (fseek(pf, 0, SEEK_END) || (file_size = ftell(pf)) < 0) { + goto out; + } + if ((size_t)file_size < sizeof(aot_header)) { + qemu_log_mask(LAT_LOG_AOT, "aot file is too short %s\n", lib_name); + remove_curr_aot_file(fd); + goto out; + } + file_sz = file_size; + + if (!aot_file_has_footer(pf, AOT_VERSION)) { + qemu_log_mask(LAT_LOG_AOT, "aot file is not complete %s\n", lib_name); + remove_curr_aot_file(fd); + goto out; + } + + fseek(pf, 0, SEEK_SET); + buffer = mmap(NULL, file_sz, PROT_READ, MAP_SHARED, fd, 0); + if (buffer == MAP_FAILED) { + qemu_log_mask(LAT_LOG_AOT, "aot file mmap error\n"); + goto out; + } + assert(buffer); + aot_header *p_header = (aot_header *)buffer; + + if (p_header->aot_file_type & (ELF_AOT_FILE | PE_AOT_FILE)) { + if (stat(lib_name, &statbuf) + || p_header->lib_size != statbuf.st_size + || p_header->last_modify_time.tv_sec != statbuf.st_mtim.tv_sec + || p_header->last_modify_time.tv_nsec != statbuf.st_mtim.tv_nsec) { + qemu_log_mask(LAT_LOG_AOT, + "need remove old aot file. %s lib_size %d %ld\n", + aot_file_path, p_header->lib_size, statbuf.st_size); + remove_curr_aot_file(fd); + goto out; + } + } + + *curr_aot_buffer = buffer; + curr_lib_info = lib_tree_insert(aot_file_name, buffer, file_sz); + +out: + if (!curr_lib_info && buffer != MAP_FAILED) { + munmap(buffer, file_sz); + } + fclose(pf); + return curr_lib_info; +} + +#endif diff --git a/target/i386/latx/sbt/file_ctx.c b/target/i386/latx/sbt/file_ctx.c index 6e316f3cfd6..0ce7b35f969 100644 --- a/target/i386/latx/sbt/file_ctx.c +++ b/target/i386/latx/sbt/file_ctx.c @@ -27,6 +27,36 @@ struct aot_info { time_t st_actime; }; +int get_aot_path(const char *lib_name, char *file_path, + size_t file_path_size) +{ + const char *home; + char *escaped_name; + int len; + + if (!lib_name || !file_path || !file_path_size) { + return -EINVAL; + } + escaped_name = g_strdup(lib_name); + if (!escaped_name) { + return -ENOMEM; + } + for (char *p = escaped_name; *p; p++) { + if (*p == '/') { + *p = '+'; + } + } + home = getenv("HOME"); + len = snprintf(file_path, file_path_size, "%s/.cache/latx/%s.aot2", + home ? home : "", escaped_name); + g_free(escaped_name); + if (len < 0 || (size_t)len >= file_path_size) { + file_path[0] = '\0'; + return -ENAMETOOLONG; + } + return 0; +} + int aot_file_get_tmp_path(const char *aot_file, char *tmp_path, size_t tmp_path_size) { diff --git a/target/i386/latx/sbt/meson.build b/target/i386/latx/sbt/meson.build index 568845b9be4..23dde43fd88 100644 --- a/target/i386/latx/sbt/meson.build +++ b/target/i386/latx/sbt/meson.build @@ -1,6 +1,7 @@ i386_ss.add(when: 'CONFIG_LATX', if_true: files( 'segment.c', 'aot.c', + 'aot_reader.c', 'file_ctx.c', 'aot_link_seg.c', 'aot_merge.c', diff --git a/target/i386/latx/sbt/tests/aot-cache-reader-test.c b/target/i386/latx/sbt/tests/aot-cache-reader-test.c new file mode 100644 index 00000000000..f78d1df77b7 --- /dev/null +++ b/target/i386/latx/sbt/tests/aot-cache-reader-test.c @@ -0,0 +1,200 @@ +#include "qemu/osdep.h" + +#include + +#include "aot.h" +#include "aot_reader.h" +#include "aot_lib.h" +#include "file_ctx.h" + +char aot_file_path_buffer[PATH_MAX]; +char aot_file_lock_buffer[PATH_MAX]; +char *aot_file_path = aot_file_path_buffer; +char *aot_file_lock = aot_file_lock_buffer; +int qemu_loglevel; +static bool fail_fdopen; +static int tracked_fd; +static int sentinel_fd; +static unsigned int fdopen_count; +static unsigned int fclose_count; + +FILE *__real_fdopen(int fd, const char *mode); +int __real_fclose(FILE *stream); +FILE *__wrap_fdopen(int fd, const char *mode); +int __wrap_fclose(FILE *stream); + +FILE *__wrap_fdopen(int fd, const char *mode) +{ + fdopen_count++; + tracked_fd = fd; + if (fail_fdopen) { + errno = EMFILE; + return NULL; + } + return __real_fdopen(fd, mode); +} + +int __wrap_fclose(FILE *stream) +{ + int ret; + + ret = __real_fclose(stream); + fclose_count++; + sentinel_fd = open("/dev/null", O_RDONLY); + g_assert_cmpint(sentinel_fd, ==, tracked_fd); + return ret; +} + +int qemu_log(const char *fmt G_GNUC_UNUSED, ...) +{ + return 0; +} + +void print_stack_trace(void) +{ +} + +void pstrcpy(char *buf, int buf_size, const char *str) +{ + g_strlcpy(buf, str, buf_size); +} + +static void reset_stream_counts(void) +{ + fail_fdopen = false; + tracked_fd = -1; + sentinel_fd = -1; + fdopen_count = 0; + fclose_count = 0; +} + +static void assert_tracked_fd_is_closed(void) +{ + g_assert_cmpint(tracked_fd, >=, 0); + errno = 0; + g_assert_cmpint(fcntl(tracked_fd, F_GETFD), ==, -1); + g_assert_cmpint(errno, ==, EBADF); +} + +static void assert_stream_closed(void) +{ + g_assert_cmpuint(fdopen_count, ==, 1); + g_assert_cmpuint(fclose_count, ==, 1); + g_assert_cmpint(sentinel_fd, ==, tracked_fd); + g_assert_cmpint(fcntl(sentinel_fd, F_GETFD), !=, -1); + g_assert_cmpint(close(sentinel_fd), ==, 0); + sentinel_fd = -1; +} + +static void write_cache(const char *name, bool has_header, bool has_footer, + char *path) +{ + g_autofree char *dir = NULL; + g_autofree uint8_t *contents = NULL; + size_t footer_size = strlen(AOT_VERSION); + size_t size = (has_header ? sizeof(aot_header) : 0) + + (has_footer ? footer_size : 0); + + g_assert(get_aot_path(name, path, PATH_MAX) == 0); + dir = g_path_get_dirname(path); + g_assert(g_mkdir_with_parents(dir, 0700) == 0); + + contents = g_malloc0(size); + if (has_header) { + ((aot_header *)contents)->aot_file_type = CACHE_AOT_FILE; + } + if (has_footer) { + memcpy(contents + size - footer_size, AOT_VERSION, footer_size); + } + g_assert(g_file_set_contents(path, (char *)contents, size, NULL)); +} + +static void remove_cache(const char *name) +{ + char path[PATH_MAX]; + char lock_path[PATH_MAX]; + + g_assert(get_aot_path(name, path, sizeof(path)) == 0); + if (g_file_test(path, G_FILE_TEST_EXISTS)) { + g_assert(g_remove(path) == 0); + } + g_assert(aot_file_get_lock_path(path, lock_path, sizeof(lock_path)) == 0); + if (g_file_test(lock_path, G_FILE_TEST_EXISTS)) { + g_assert(g_remove(lock_path) == 0); + } +} + +int main(void) +{ + g_autofree char *cache_dir = NULL; + g_autofree char *cache_parent = NULL; + g_autofree char *test_dir = NULL; + g_autofree char *old_home = NULL; + char lib_name[] = "test-library"; + char bad_footer_name[] = "bad-footer"; + char truncated_name[] = "truncated"; + char complete_name[] = "complete"; + char fdopen_failure_name[] = "fdopen-failure"; + char cache_path[PATH_MAX]; + void *buffer; + lib_info *lib; + + test_dir = g_dir_make_tmp("latx-aot-reader-XXXXXX", NULL); + g_assert(test_dir != NULL); + old_home = g_strdup(g_getenv("HOME")); + g_assert(g_setenv("HOME", test_dir, true)); + + lib_tree_init(); + for (int i = 0; i < 16; i++) { + reset_stream_counts(); + write_cache(bad_footer_name, true, false, cache_path); + g_assert_cmpint(aot_get_tb_num(lib_name, bad_footer_name, NULL), + ==, 0); + g_assert(!g_file_test(cache_path, G_FILE_TEST_EXISTS)); + assert_stream_closed(); + + reset_stream_counts(); + buffer = NULL; + write_cache(truncated_name, false, true, cache_path); + g_assert(aot_load(lib_name, truncated_name, &buffer) == NULL); + g_assert(buffer == NULL); + g_assert(!g_file_test(cache_path, G_FILE_TEST_EXISTS)); + assert_stream_closed(); + } + + reset_stream_counts(); + buffer = NULL; + write_cache(complete_name, true, true, cache_path); + lib = aot_load(lib_name, complete_name, &buffer); + g_assert(lib != NULL); + g_assert(buffer != NULL); + assert_stream_closed(); + g_assert(lib_tree_remove(complete_name)); + + reset_stream_counts(); + fail_fdopen = true; + buffer = NULL; + write_cache(fdopen_failure_name, true, true, cache_path); + g_assert(aot_load(lib_name, fdopen_failure_name, &buffer) == NULL); + g_assert(buffer == NULL); + g_assert_cmpuint(fdopen_count, ==, 1); + g_assert_cmpuint(fclose_count, ==, 0); + assert_tracked_fd_is_closed(); + + remove_cache(bad_footer_name); + remove_cache(truncated_name); + remove_cache(complete_name); + remove_cache(fdopen_failure_name); + cache_dir = g_build_filename(test_dir, ".cache", "latx", NULL); + g_assert(g_rmdir(cache_dir) == 0); + cache_parent = g_build_filename(test_dir, ".cache", NULL); + g_assert(g_rmdir(cache_parent) == 0); + + if (old_home) { + g_assert(g_setenv("HOME", old_home, true)); + } else { + g_unsetenv("HOME"); + } + g_assert(g_rmdir(test_dir) == 0); + return 0; +}