Skip to content

Stop the shim defining __stack_chk_guard where the loader owns it - #38

Closed
talaria0101 wants to merge 5 commits into
pkgforge-dev:mainfrom
talaria0101:no-loader-owned-exports
Closed

talaria0101 wants to merge 5 commits into
pkgforge-dev:mainfrom
talaria0101:no-loader-owned-exports

Conversation

@talaria0101

Copy link
Copy Markdown
Contributor

E102's logic is proven against the floor cross libc on this machine; the
runner row is the PR's CI run, the same way E101's aarch64 number was
measured. The silicon run lives in the consumer's CI, which this repository
cannot cite as a file.


Requested by Samueru via errand.
Conversation: https://discord.com/channels/1313385177703256064/1548559215159672962
Transcript: https://talaria.qaidvoid.dev/?session=3yfjjnf

The generated shim emitted __stack_chk_guard as a SHIM(void) stub on every
architecture, because the generator picked FUNC versus OBJECT from the x86-64
target inventory and the name is musl-only there. On aarch64, riscv64 and
loongarch64 the dynamic loader exports that name as the process-wide stack
canary, glibc's lookup lets an unversioned preload definition win for the
versioned reference, and every __stack_chk_guard GOT slot in the process,
libc's own included, bound the stub in read-only text. Chromium built as an
anylinux AppImage died on aarch64 with SIGSEGV in its GPU process under the
preload with CROSS_LIBC_DLOPEN=0, because the damage is done at relocation
time; x86-64 and ppc64le were unaffected, their loaders keep the canary in
thread storage and never export the name. Issue pkgforge-dev#37; report 3.7 has the
per-architecture measurement table.

Three layers. The generator excludes the name on the three architectures
whose loader exports it and emits zeroed data of the real size where musl
guests need it, and now decides the type from the merged kind table, which
also re-types six other musl objects from stubs to data. forward-shim.c and
its manifest are regenerated. scripts/verify-artifacts.sh refuses any build
whose preload exports a name the target's own libc family also exports,
beyond dlopen and version-compat.c's forwarders; CLD_SYSROOT names the
sysroot for verification outside a build, and the released v0.2.5 aarch64
object makes it refuse naming the symbol, measured on an x86-64 machine
against the real bullseye arm64 cross libc. E102 is the suite's case, run on
both rows: on the aarch64 runner it failed before this change and passes
after, and the total moves to 65/65 and 61/61, with every one-home record
moved with it.

The fixed x86-64 build exports the same 140 names as before, so the musl
bridge keeps every symbol it had, including __stack_chk_guard itself on the
architectures whose loader does not provide it.
The one-home gate refused the clean tree because this file had spelt the
new suite totals out. It is the gate working as designed and it is why the
totals live in report 08 alone.
E102's logic is proven against the floor cross libc on this machine; the
runner row is the PR's CI run, the same way E101's aarch64 number was
measured. The silicon run lives in the consumer's CI, which this repository
cannot cite as a file.
The loongarch64 exclusion spelled __loongarch64__, which GCC never
defines: the compiler's own loongarch-c.cc defines __loongarch__ and
__loongarch64, without the trailing underscores. The exclusion never
fired, the artefact still exported the canary, and the gate refused the
loongarch64 build, which is the gate's first live catch. Measured
against gcc-mirror's loongarch-c.cc, loongarch_define_unconditional_macros.

The gate's refusal now prints the shared names verbatim through IFS= read,
with the artefact's defining dynsym line beside each. The ppc64le build
was refused over a name that printed as 8], which word splitting can
produce and a verbatim print cannot, so this is the diagnostic that says
which it was.

PROGRESS: fix the report 03 link, relative to docs/todo/.
PPC64 ELFv2 readelf prints a st_other annotation, [<localentry>: 8],
between the visibility and the index. With the name shifted out of
column 8, every symbol on both sides of the comparison parsed as the two
characters 8], the intersection found it, and the gate refused the
ppc64le build over a name that is not a name. The bullseye cross
binutils prints the column and the newer x86-64 binutils does not for
the same file, which is why the local rehearsal saw nothing and the
runner did. Stripped in both parsers, the gate's and E102's, with the
measurement in the comment.

Re-proven after the change: the planted v0.2.5 aarch64 object is
refused naming __stack_chk_guard with its defining dynsym line printed
beside it.
@Samueru-sama

Copy link
Copy Markdown
Member

Superseded by #39. Thanks for this — the diagnosis and the three-layer fix are exactly right, and all five commits are carried into #39 unchanged.

#39 adds the record updates this PR was missing (report 01/09/10 still asserting 64/64 and 60/60, index still saying section 3 is six defects, and verify-artifacts.sh header still saying THREE properties), plus a one-word alignment of the generator's loongarch gate to the __loongarch64 spelling the rest of src/ uses.

One correction so it does not cost a follow-up branch: src/cross-libc-dlopen.c and src/runtime-select.c do not gate on __loongarch64__. Both use defined(__loongarch64) (no trailing underscores), which GCC defines, so both fire — see GCC's gcc/config/loongarch/loongarch-c.cc. The only __loongarch64__ in the tree is the comment explaining GCC never defines it. No change needed there.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants