Repository: rizinorg/rizin (2026-03-23T14:07:03Z)

Issue:
Wrong `__stack_chk_guard` naming in ARMv7 binaries
### Work environment

| Questions                                            | Answers
|------------------------------------------------------|--------------------
| OS/arch/bits (mandatory)                             | Debian ARMv7
| File format of the file you reverse (mandatory)      | ELF
| Architecture/bits of the file (mandatory)            | ARMv7 32bit
| `rizin -v` full output, **not truncated** (mandatory)  | rizin 0.4.0-git @ linux-x86-64
commit: f40b5ef545c1956971411cd88bc269dbea78dea2, build: 2022-01-14__17:10:21


### Expected behavior

`aav.aav.0x000113b0` should become `obj.__stack_chk_guard__GLIBC_2.4` or just `__stack_chk_guard`

### Actual behavior

```
[0x000105f0]> s sym.CWE190_Integer_Overflow__char_fscanf_add_01_bad 
[0x000106e0]> pdf
            ; CALL XREF from main @ 0x10798
╭ sym.CWE190_Integer_Overflow__char_fscanf_add_01_bad ();
│           ; var int32_t var_ah @ fp-0xa
│           ; var int32_t var_9h @ fp-0x9
│           ; var int32_t var_8h @ fp-0x8
│           0x000106e0      push  {fp, lr}
│           0x000106e4      add   fp, sp, 4
│           0x000106e8      sub   sp, sp, 8
│           0x000106ec      ldr   r3, [aav.aav.0x000113b0]             ; [0x113b0:4]=0x21f0c obj.__stack_chk_guard__GLIBC_2.4
│           0x000106f0      ldr   r3, [r3]                             ; 0x113b0
│                                                                      ; aav.0x000113b0
│           0x000106f4      str   r3, [var_8h]                         ; 8
│           0x000106f8      mov   r3, 0
│           0x000106fc      mov   r3, 0x20
│           0x00010700      strb  r3, [var_ah]                         ; 0xa ; 10
│           0x00010704      ldr   r3, [obj.stdin]                      ; [0x22060:4]=0
│                                                                      ; obj.stdin__GLIBC_2.4
│           0x00010708      ldr   r3, [r3]                             ; 0x22060
│                                                                      ; obj.stdin__GLIBC_2.4
│           0x0001070c      sub   r2, fp, 0xa
│           0x00010710      ldr   r1, [str.c]                          ; [0x113ac:4]=0x6325 ; "%c" ; const char *format
│           0x00010714      mov   r0, r3                               ; FILE *stream
│           0x00010718      bl    sym.imp.__isoc99_fscanf              ; int fscanf(FILE *stream, const char *format, void *va_args)
│           0x0001071c      ldrb  r3, [var_ah]                         ; 0xa ; 10
│           0x00010720      add   r3, r3, 1
│           0x00010724      strb  r3, [var_9h]                         ; 9
│           0x00010728      ldrb  r3, [var_9h]                         ; 9
│           0x0001072c      mov   r0, r3                               ; int32_t arg1
│           0x00010730      bl    sym.printHexCharLine
│           0x00010734      mov   r0, r0
│           0x00010738      ldr   r3, [aav.aav.0x000113b0]             ; [0x113b0:4]=0x21f0c obj.__stack_chk_guard__GLIBC_2.4
│           0x0001073c      ldr   r2, [r3]                             ; 0x113b0
│                                                                      ; aav.0x000113b0
│           0x00010740      ldr   r3, [var_8h]                         ; 8
│           0x00010744      eors  r2, r3, r2
│           0x00010748      mov   r3, 0
│       ╭─< 0x0001074c      beq   0x10754
│       │   0x00010750      bl    sym.imp.__stack_chk_fail             ; void __stack_chk_fail(void)
│       ╰─> 0x00010754      sub   sp, fp, 4
╰           0x00010758      pop   {fp, pc}
[0x000106e0]> 
```

### Steps to reproduce the behavior
```
[0x000105f0]> aaa
[0x000105f0]> s sym.CWE190_Integer_Overflow__char_fscanf_add_01_bad 
[0x000106e0]> pdf
```

### Additional Logs, screenshots, source code,  configuration dump, ...

[rizin-2205-issue-binary.zip](https://github.com/rizinorg/rizin/files/7869912/rizin-2205-issue-binary.zip)




Discussion:
@XVilka, is this issue still open? I want to work on this.
@parthbansaliitr sure, go ahead
@notxvilka double check this, just in case



Patch:
diff --git a/librz/core/disasm.c b/librz/core/disasm.c
index 9b196fd4c3f..a68512a28f2 100644
--- a/librz/core/disasm.c
+++ b/librz/core/disasm.c
@@ -319,6 +319,7 @@ static char *ds_esc_str(RzDisasmState *ds, const char *str, int len, const char
 static void ds_print_ptr(RzDisasmState *ds, int len, int idx);
 static void ds_print_str(RzDisasmState *ds, const char *str, int len, ut64 refaddr);
 static void ds_opstr_sub_jumps(RzDisasmState *ds);
+static void ds_opstr_resolve_aav_symbols(RzDisasmState *ds);
 static void ds_start_line_highlight(RzDisasmState *ds);
 static void ds_end_line_highlight(RzDisasmState *ds);
 static bool line_highlighted(RzDisasmState *ds);
@@ -876,6 +877,54 @@ static void __replaceImports(RzDisasmState *ds) {
 	}
 }
 
+static void ds_opstr_resolve_aav_symbols(RzDisasmState *ds) {
+	if (!ds->opstr || !ds->core || !ds->core->flags || !ds->core->io || !ds->analysis_op.refptr) {
+		return;
+	}
+	RzCore *core = ds->core;
+	const char *pattern = "aav.aav.";
+	char *pos = strstr(ds->opstr, pattern);
+	if (!pos) {
+		return;
+	}
+
+	char *addr_start = pos + strlen(pattern);
+	char *addr_end = addr_start;
+	while (*addr_end == 'x' || IS_HEXCHAR(*addr_end)) {
+		addr_end++;
+	}
+	if (addr_end == addr_start) {
+		return;
+	}
+
+	char addr_str[32] = { 0 };
+	const size_t addr_len = RZ_MIN((size_t)(addr_end - addr_start), sizeof(addr_str) - 1);
+	memcpy(addr_str, addr_start, addr_len);
+	ut64 aav_addr = rz_num_get(NULL, addr_str);
+	if (!aav_addr || aav_addr < ds->min_ref_addr) {
+		return;
+	}
+
+	RzFlagItem *f1 = rz_flag_get_preferred_item(core->flags, aav_addr);
+	if (!f1 || !rz_str_startswith(f1->name, "aav.")) {
+		return;
+	}
+
+	ut8 hop_buf[sizeof(ut64)] = { 0 };
+	if (!rz_io_read_at_mapped(core->io, aav_addr, hop_buf, ds->analysis_op.refptr)) {
+		return;
+	}
+	ut64 dereferenced = rz_read_ble(hop_buf, core->print->big_endian, ds->analysis_op.refptr * 8);
+	RzFlagItem *f2 = rz_flag_get_preferred_item(core->flags, dereferenced);
+	if (!f2 || rz_str_startswith(f2->name, "aav.")) {
+		return;
+	}
+
+	char pattern_to_replace[64] = { 0 };
+	rz_strf(pattern_to_replace, "aav.aav.%s", addr_str);
+	ds->opstr = rz_str_replace(ds->opstr, pattern_to_replace, f2->name, 1);
+}
+
 static void ds_opstr_try_colorize(RzDisasmState *ds, bool print_color) {
 	bool colorize_asm = print_color && ds->show_color && ds->colorop;
 	if (!colorize_asm) {
@@ -1054,6 +1103,7 @@ static void ds_build_op_str(RzDisasmState *ds, bool print_color) {
 		core->parser->flagspace = ofs;
 		free(ds->opstr);
 		ds->opstr = rz_str_dup(ds->str);
+		ds_opstr_resolve_aav_symbols(ds);
 	} else {
 		ds_opstr_try_colorize(ds, print_color);
 	}
diff --git a/librz/flag/flag.c b/librz/flag/flag.c
index f832ada926f..d728ef513d0 100644
--- a/librz/flag/flag.c
+++ b/librz/flag/flag.c
@@ -436,6 +436,47 @@ RZ_API RzFlagItem *rz_flag_get_by_spaces(RzFlag *f, ut64 off, ...) {
 	return ret ? evalFlag(f, ret) : NULL;
 }
 
+static bool is_auto_aav_flag(const RzFlagItem *flag) {
+	return flag && !RZ_STR_ISEMPTY(flag->name) && rz_str_startswith(flag->name, "aav.");
+}
+
+/**
+ * \brief Get the preferred flag item at an offset.
+ *
+ * The preferred item follows the standard space priority and avoids returning
+ * auto-generated `aav.*` entries when a non-`aav.*` fallback exists at the
+ * same offset.
+ *
+ * \param f The flag instance.
+ * \param off The offset to query.
+ *
+ * \return The preferred flag item, or NULL if none exists.
+ */
+RZ_API RZ_BORROW RzFlagItem *rz_flag_get_preferred_item(RZ_NONNULL RzFlag *f, ut64 off) {
+	rz_return_val_if_fail(f, NULL);
+
+	RzFlagItem *preferred = rz_flag_get_by_spaces(f, off,
+		"symbols",
+		"imports",
+		"relocs",
+		"symbols.sections",
+		"functions",
+		"globals",
+		"strings",
+		"resources",
+		"sections",
+		"segments",
+		NULL);
+	if (!preferred || !is_auto_aav_flag(preferred)) {
+		return preferred;
+	}
+	RzFlagItem *fallback = rz_flag_get_i(f, off);
+	if (fallback && !is_auto_aav_flag(fallback)) {
+		return fallback;
+	}
+	return preferred;
+}
+
 static bool isFunctionFlag(const char *n) {
 	return (!strncmp(n, "sym.func.", 9) || !strncmp(n, "method.", 7) || !strncmp(n, "sym.", 4) || !strncmp(n, "func.", 5) || !strncmp(n, "fcn.0", 5));
 }
diff --git a/librz/include/rz_flag.h b/librz/include/rz_flag.h
index a6cb9f2ca3e..75cd9120c7c 100644
--- a/librz/include/rz_flag.h
+++ b/librz/include/rz_flag.h
@@ -101,6 +101,7 @@ RZ_API bool rz_flag_exist_at(RzFlag *f, const char *flag_prefix, ut16 fp_size, u
 RZ_API RzFlagItem *rz_flag_get(RzFlag *f, const char *name);
 RZ_API RzFlagItem *rz_flag_get_i(RzFlag *f, ut64 off);
 RZ_API RzFlagItem *rz_flag_get_by_spaces(RzFlag *f, ut64 off, ...);
+RZ_API RZ_BORROW RzFlagItem *rz_flag_get_preferred_item(RZ_NONNULL RzFlag *f, ut64 off);
 RZ_API RzFlagItem *rz_flag_get_at(RzFlag *f, ut64 off, bool closest);
 RZ_API RZ_BORROW RzFlagItem *rz_flag_get_at_by_spaces(RZ_NONNULL RzFlag *f, bool closest, ut64 off, ...);
 RZ_API RzList /*<RzFlagItem *>*/ *rz_flag_all_list(RzFlag *f, bool by_space);


Test patch:
diff --git a/test/unit/test_analysis_op.c b/test/unit/test_analysis_op.c
index 1d9c0237d8f..87a937452ee 100644
--- a/test/unit/test_analysis_op.c
+++ b/test/unit/test_analysis_op.c
@@ -180,10 +180,63 @@ bool test_rz_core_print_disasm() {
 	mu_end;
 }
 
+bool test_rz_core_print_disasm_resolve_aav_symbols() {
+	RzCore *core = rz_core_new();
+	mu_assert_notnull(core, "rz_core_new failed");
+	RzIODesc *io_desc = rz_io_open_at(core->io, "malloc://0x200000", RZ_PERM_RWX, 0644, 0, NULL);
+	mu_assert_notnull(io_desc, "io open failed");
+	bool arch_configured = rz_core_arch_configure(core, "x86", 32, NULL, NULL, NULL);
+	mu_assert_true(arch_configured, "rz_core_arch_configure failed");
+	bool analysis_arch_set = rz_analysis_use(core->analysis, "x86");
+	mu_assert_true(analysis_arch_set, "rz_analysis_use failed");
+	bool analysis_bits_set = rz_analysis_set_bits(core->analysis, 32);
+	mu_assert_true(analysis_bits_set, "rz_analysis_set_bits failed");
+	rz_config_set_i(core->config, "scr.color", 0);
+	rz_config_set_b(core->config, "asm.sub.names", true);
+	rz_config_set_b(core->config, "asm.sub.rel", true);
+	rz_config_set_i(core->config, "asm.sub.varmin", 0);
+
+	ut8 code[] = { 0xa1, 0x20, 0x00, 0x10, 0x00 }; // mov eax, dword [0x100020]
+	ut8 ptr[] = { 0x40, 0x00, 0x10, 0x00 }; // *(0x100020) = 0x100040
+	rz_io_write_at(core->io, 0x100020, ptr, sizeof(ptr));
+
+	rz_flag_space_set(core->flags, "symbols");
+	rz_flag_set(core->flags, "aav.0x00100020", 0x100020, 4);
+	rz_flag_set(core->flags, "obj.__stack_chk_guard", 0x100040, 4);
+
+	RzPVector *vec = rz_pvector_new((RzPVectorFree)rz_analysis_disasm_text_free);
+	mu_assert_notnull(vec, "rz_core_print_disasm vec not null");
+	RzCoreDisasmOptions options = {
+		.vec = vec,
+		.cbytes = 1,
+	};
+	rz_core_print_disasm(core, 0, code, sizeof(code), sizeof(code), NULL, &options);
+
+	size_t line_count = rz_pvector_len(vec);
+	mu_assert_true(line_count >= 1, "rz_core_print_disasm should produce at least one line");
+	RzAnalysisDisasmText *t = rz_pvector_at(vec, 0);
+	mu_assert_notnull(t, "first disasm line");
+	char *insn = rz_str_dup(t->text);
+	mu_assert_notnull(insn, "instruction line copy");
+	char *comment = strchr(insn, ';');
+	if (comment) {
+		*comment = '\0';
+	}
+	mu_assert_strcontains(insn, "obj.__stack_chk_guard", "aav.aav symbol should be resolved to preferred symbol");
+	char *aav_symbol = strstr(insn, "aav.aav.");
+	mu_assert_null(aav_symbol, "aav.aav symbol should not be present in instruction operand");
+	free(insn);
+
+	rz_core_free(core);
+	rz_pvector_free(vec);
+	mu_end;
+}
+
 int all_tests() {
 	mu_run_test(test_rz_analysis_op_val);
 	mu_run_test(test_rz_core_analysis_bytes);
 	mu_run_test(test_rz_core_print_disasm);
+	mu_run_test(test_rz_core_print_disasm_resolve_aav_symbols);
 	return tests_passed != tests_run;
 }
 
diff --git a/test/unit/test_flags.c b/test/unit/test_flags.c
index 1afff21745b..89119dc9946 100644
--- a/test/unit/test_flags.c
+++ b/test/unit/test_flags.c
@@ -169,11 +169,68 @@ bool test_rz_flag_set_next() {
 	mu_end;
 }
 
+bool test_rz_flag_get_preferred_item() {
+	RzFlag *flags = rz_flag_new();
+	mu_assert_notnull(flags, "rz_flag_new() failed");
+
+	ut64 off = 0x1337;
+	rz_flag_space_set(flags, "symbols");
+	rz_flag_set(flags, "sym.real_name", off, 0);
+	rz_flag_space_set(flags, "custom");
+	rz_flag_set(flags, "custom.name", off, 0);
+
+	RzFlagItem *fi = rz_flag_get_preferred_item(flags, off);
+	mu_assert_notnull(fi, "preferred flag should exist");
+	mu_assert_streq(fi->name, "sym.real_name", "must prefer symbol space item");
+
+	rz_flag_free(flags);
+	mu_end;
+}
+
+bool test_rz_flag_get_preferred_item_fallback_non_aav() {
+	RzFlag *flags = rz_flag_new();
+	mu_assert_notnull(flags, "rz_flag_new() failed");
+
+	ut64 off = 0x4242;
+	rz_flag_space_set(flags, "symbols");
+	rz_flag_set(flags, "aav.0x00004242", off, 0);
+	rz_flag_space_set(flags, "custom");
+	rz_flag_set(flags, "obj.__stack_chk_guard", off, 0);
+
+	RzFlagItem *fi = rz_flag_get_preferred_item(flags, off);
+	mu_assert_notnull(fi, "preferred flag should exist");
+	mu_assert_streq(fi->name, "obj.__stack_chk_guard", "must fallback to non-aav item");
+
+	rz_flag_free(flags);
+	mu_end;
+}
+
+bool test_rz_flag_get_preferred_item_keep_aav_when_only_aav() {
+	RzFlag *flags = rz_flag_new();
+	mu_assert_notnull(flags, "rz_flag_new() failed");
+
+	ut64 off = 0x5151;
+	rz_flag_space_set(flags, "symbols");
+	rz_flag_set(flags, "aav.0x00005151", off, 0);
+	rz_flag_space_set(flags, "custom");
+	rz_flag_set(flags, "aav.0x0000beef", off, 0);
+
+	RzFlagItem *fi = rz_flag_get_preferred_item(flags, off);
+	mu_assert_notnull(fi, "preferred flag should exist");
+	mu_assert_streq(fi->name, "aav.0x00005151", "must keep preferred aav when no non-aav fallback exists");
+
+	rz_flag_free(flags);
+	mu_end;
+}
+
 int all_tests(void) {
 	mu_run_test(test_rz_flag_get_set);
 	mu_run_test(test_rz_flag_by_spaces);
 	mu_run_test(test_rz_flag_get_at);
 	mu_run_test(test_rz_flag_set_next);
+	mu_run_test(test_rz_flag_get_preferred_item);
+	mu_run_test(test_rz_flag_get_preferred_item_fallback_non_aav);
+	mu_run_test(test_rz_flag_get_preferred_item_keep_aav_when_only_aav);
 	return tests_passed != tests_run;
 }