commit dc1dc1f96640de20daa2a4b9accfb0a26b839218
parent 693766af7c518ae065bcfabbbc9f22692bb47d79
Author: Ryan Sepassi <rsepassi@gmail.com>
Date: Tue, 16 Jun 2026 15:35:46 -0700
link: parse linker-script integer literals as unsigned (fix high-half UB)
The hex/decimal accumulator was a signed i64, so a literal with the top bit
set (e.g. a high-half kernel ORIGIN 0xFFFFFFFF80000000) triggered signed
left-shift/overflow UB, aborting under UBSan before layout. Accumulate in u64
and reinterpret into the i64 int_val; addresses are bit patterns.
Diffstat:
1 file changed, 8 insertions(+), 5 deletions(-)
diff --git a/src/link/link_script.c b/src/link/link_script.c
@@ -348,7 +348,10 @@ static KitLinkExpr* parse_atom(LSP* p);
static KitLinkExpr* parse_int(LSP* p) {
KitLinkExpr* e;
size_t start = p->pos;
- i64 v = 0;
+ /* Accumulate unsigned: linker-script addresses routinely set the top bit
+ * (e.g. 0xFFFFFFFF80000000 high-half kernels); a signed shift/multiply would
+ * be UB there. The value is a bit pattern, reinterpreted into int_val below. */
+ u64 v = 0;
if (p->pos + 1 < p->len && p->src[p->pos] == '0' &&
(p->src[p->pos + 1] == 'x' || p->src[p->pos + 1] == 'X')) {
p->pos += 2;
@@ -387,20 +390,20 @@ static KitLinkExpr* parse_int(LSP* p) {
if (p->pos < p->len) {
char suffix = p->src[p->pos];
if (suffix == 'K' || suffix == 'k') {
- v *= 1024ll;
+ v *= 1024ull;
++p->pos;
} else if (suffix == 'M' || suffix == 'm') {
- v *= 1024ll * 1024ll;
+ v *= 1024ull * 1024ull;
++p->pos;
} else if (suffix == 'G' || suffix == 'g') {
- v *= 1024ll * 1024ll * 1024ll;
+ v *= 1024ull * 1024ull * 1024ull;
++p->pos;
}
}
e = lsp_new_expr(p);
if (!e) return NULL;
e->kind = KIT_LE_INT;
- e->v.int_val = v;
+ e->v.int_val = (i64)v;
return e;
}