Skip to content

Unify around alr; Termux CLI first (Ubuntu 26.04, tawcroot lane) - #25

Merged
Meapri merged 36 commits into
mainfrom
docs/unified-direction
Oct 11, 2026
Merged

Meapri merged 36 commits into
mainfrom
docs/unified-direction

Conversation

@Meapri

@Meapri Meapri commented Oct 11, 2026

Copy link
Copy Markdown
Owner

Summary

Unifies the project around the alr engine and makes Termux CLI the first target.

  • ADR 0004 / ADR 0005: one engine (alr), Termux-first goals T1–T5.
  • T1: Ubuntu 26.04 LTS is the default and only supported guest; Android 17 is supported.
  • T5: static and Go guest binaries (codex, Go toolchain, busybox-static) run in the tawcroot lane (alr-lane, ADR 0010), with path virtualization. Lane cost brought within the T2 budget (direct guest-memory copies, one-name kernel lookups, BPF trap filtering).
  • Fixes: fork-then-vfork signal shadow, release lookup outside $PREFIX, AppleDouble members in the release tarball, /proc/<pid>/fd keeps its natural fd number.
  • Docs: Termux install guide, evidence for every measured number, ADR 0011 (svc rewrite, Proposed only).

Verification (device: SM-S937N, Android 17)

  • Acceptance: PASS=221 FAIL=0 KNOWN_FAIL=1 SKIP=0 (KNOWN_FAIL is /dev/full, which native Termux also cannot open).
  • Breadth 96/96, supervisor suite 13/13, T2 workloads within 1.2x.
  • make check passes; full pytest has the same 24 failures as the base (legacy Android app/proot tests).

Known limits

  • Measured on Samsung devices only.
  • Lane processes show SIGSEGV/SIGBUS in SigCgt (ADR 0010 decision 7).
  • Lane find/ls -lR sit near the T2 budget (1.08–1.21x across runs).

🤖 Generated with Claude Code

claude and others added 30 commits October 11, 2026 15:40
Record the integrated direction and bring stale documents in line with
build 0.4.163 (targetSdk 28, alr engine, PRoot removed).

- Add ADR 0004: single product engine (alr), three layers (engine,
  host services, delivery surfaces), decisions D1-D8, phase plan P0-P4,
  and the unevaluated in-process SECCOMP_RET_TRAP lane for raw-syscall
  binaries with a measurable decision table.
- Add docs/adr/README.md explaining the three ADR namespaces.
- Rewrite README.md and docs/architecture.md; rewrite
  docs/architecture/gpu-display-bridge.md to the real stack.
- Revise product-requirements.md and research/prior-art.md (tawc,
  glibc-runner, alr engine; PRoot removed).
- Label PERFORMANCE.md numbers as legacy-loader measurements; GPU
  re-measurement on the alr engine is open (P1).
- Add status banners to historical specs; amend ADR 0001 status.
- Fix runtime/alr README relationship section; add an ADR 0006 addendum.
- Add tests/test_unified_direction_docs.py (decision record and link guard).

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ea46k1MKywZKnRp7wXsFUU
Vendor wmww/tawc tawcroot/ at d9efb4a (src, include, perf, LICENSE.MIT),
unmodified and not wired into any build or package. Record provenance,
rules, host build check and reference numbers in VENDORING.md; correct the
tawc licensing statement (sources MIT, only the tawc APK is GPLv3) in
ADR 0004, prior-art and THIRD-PARTY.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ea46k1MKywZKnRp7wXsFUU
trap_cost.c reproduces tawcroot's filter shape (only the trampoline's
return PC is allowed, everything else traps) without its path
translation, on the same syscall and iteration count as notif_cost.c.
It reports baseline, filter evaluation, signal round trip and the
re-issue round trip, applies the D6 table to (re-issue - baseline), and
withholds the verdict outside a zygote-forked app context.

tawcroot itself cannot produce this number: it does not trap getppid.
Its aarch64 build also needs bionic headers; a musl cross-compile fails,
which VENDORING.md now records.

dev-push.sh trap-cost builds and runs it in Termux.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…CEED

Spike S2 measured on the Snapdragon reference device (SM-S937N, now
Android 17) in a Termux shell (untrusted_app_27, Seccomp=2, zygote
filter stacked). Ten runs, unpinned and pinned to CPU 0 and CPU 6: the
re-issue round trip minus baseline was +410 to +925 ns, all under the
5 us row of the ADR 0004 D6 table, and over 170x cheaper than
USER_NOTIF's 154 us.

This is the mechanism alone. Path translation, the static-binary loader
and the MediaTek device remain unmeasured, and ADR 0006's non-goal
stands until a loader prototype and a new ADR exist.

The device having moved to Android 17 is recorded as an observation:
ADR 0007 scopes support to Android 16, so the sweep and acceptance runs
need repeating there.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
glibc 2.42 implements tcgetattr, tcsetattr, isatty and ttyname with
TCGETS2 from inside libc, so those calls never reach the PLT-level ioctl
wrapper that already translated TCGETS2, and Android's ioctl filter
denies them with EACCES. On Ubuntu 26.04 every terminal therefore looked
like a non-tty: tmux 3.6 panes _exit(1)ed on tcgetattr before exec, and
`tty` printed "not a tty".

Wrap tcgetattr/tcsetattr/isatty on TCGETS/TCSETS*, and make ttyname_r
check the fd the same way and read the name from /proc/self/fd on
glibc >= 2.42. The same release changed speed_t from the CBAUD code to
the baud rate, so the speed fields follow the running glibc's
convention (gnu_get_libc_version); a rate with no CBAUD code fails with
EINVAL instead of being silently dropped.

The acceptance suite gains pty-slave checks for termios, ttyname and
speed, via script(1).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
`alr install` with no distro now provisions ubuntu-26.04 (26.04.1 is
what discovery picks today). Every other command without a distro uses
the first installed tree of ubuntu-26.04, ubuntu-24.04, so a device
provisioned before this change keeps running its 24.04 instead of
failing rootfs-missing; the app resolves its guest the same way.
`alr config` reports that resolved value.

The offline fallback pin stays 24.04-only, so an install of the new
default with SHA256SUMS unreachable stops with discovery-failed rather
than fetching an unverified image; INSTALL.md says so.

INSTALL.md also lists Android 17 as supported (ADR 0007 addendum).

The device scripts default to 26.04 too, and ALR GIT VERSION asserts the
shape of the version line instead of 24.04's 2.43.0, as the node check
already does.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
ucf 3.0052 (Ubuntu 26.04) re-execs itself with a cleaned environment,
dropping ALR_ROOT, ALR_FAKEROOT and the rest. The child then ran
without path virtualization and as the real uid, so package configure
steps (ucf, php, mercurial, sysstat, parallel) failed.

The preload now records the nine runtime variables at init and the
execve wrapper appends any that the new environment lacks. Values the
guest set itself are left alone.

Acceptance gains ALR ENV SCRUB KEEPS RUNTIME (env -i id -u under
fakeroot must still print 0).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
ADR 0005 keeps one supported guest, the latest LTS. Commands without a
distro now always use ubuntu-26.04 instead of preferring whichever LTS
tree is installed, and ALR_UBUNTU_PIN goes away with the preference
list. Release discovery failing on SHA256SUMS now stops with
discovery-failed for every release; the hash-less 24.04 fallback name
is removed. A 24.04 tree still runs when named with -d, unverified.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The zygote sweep on the Snapdragon reference device after its 16 -> 17
upgrade is identical to 16's (239 syscalls), and acceptance passes on
17 (ADR 0007 addendum). P0 now accepts 16 and 17.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…droid 17

- ADR 0005 (Accepted): goals T1-T5, priority T1 -> T5 -> T3 -> T4 with
  T2 as a gate, latest-LTS-only guest policy, Termux CLI as the product,
  an extension APK for GUI/GPU, and the standalone APK frozen.
- ADR 0004 and the ADR index note the partial supersession.
- runtime/alr ADR 0007 addendum: Android 17 sweep identical, now
  supported. Sweep file for the Snapdragon 8 Elite on 17 added.
- INSTALL, 06-cli-spec and 00-product: 26.04 is the only supported
  guest, no hash-less discovery fallback, Android 16 and 17.
- T1 evidence (engine=alr): 26.04.1 acceptance PASS 164, FAIL 2 (codex
  doctor output format), KNOWN_FAIL 1, SKIP 1; breadth 96/96; the glibc
  2.42 termios and ucf environment fixes. breadth.sh uses the 26.04
  package names bind9-dnsutils and 7zip.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ale check

codex 0.162 no longer executes git or rg in `codex doctor`; it prints
"found; execution not verified". ALR CODEX FINDS GUEST GIT/RG now match
the discovery lines, and the new CLI HOSTBIN RUNS GUEST RG runs rg
through the hostbin wrapper so execution stays asserted for both tools
(git already was, by CLI HOSTBIN RUNS GUEST TOOL).

CLI VERSION DETECTS STALE PRELOAD needed a second .so from
$PREFIX/share/alr and so always skipped on a dev push. `alr version`
only compares hashes, so the check now appends bytes to the guest copy,
expects rc=1, and restores it.

Ubuntu 26.04.1 on Android 17: PASS 169, FAIL 0, KNOWN_FAIL 1 (/dev/full,
non-goal), SKIP 0, which meets ADR 0005 T1.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
RET_TRAP puts the returning filter's SECCOMP_RET_DATA in si_errno. The
zygote filter's is 0 (measured on Android 17, SM8750); anything else
comes from a filter the guest stacked on top, such as an in-process
path lane or the guest's own sandbox, and its handler must run. Before
this the supervisor answered those traps from the emulation table, so
tawcroot's path traps came back -ENOSYS and nothing loaded.

Where both filters trap, the kernel reports the later filter's data, so
the guest sees those too; a call its handler re-issues that the zygote
blocks traps again with data 0 and is rescued as before.

test_supervisor gains case 11 (guest filter trap passthrough), which
failed with rc=3 before the change. trap_cost now marks its trap the
same way so it can also be run under `alr run`.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… guest auxv

Two local changes for the ADR 0005 T5 coexistence spike, recorded in
VENDORING.md as rule 3 requires. Still not built into the product.

- The filter's RET_TRAP carries TAWCROOT_TRAP_DATA so alr's supervisor
  can tell tawcroot's traps from the zygote filter's (data 0).
- prctl(PR_GET_AUXV) returns the auxv the loader built for the guest.
  Passed through it returned tawcroot's own, whose AT_EXECFN names
  tawcroot or the --exec-child fd, and uutils (which names itself from
  it via rustix) failed with "unknown program 'tawcroot'".

VENDORING also records the aarch64 device build (Termux clang 21 with
-mno-outline-atomics) and points at the device workload numbers.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…t stay traced

On the Snapdragon reference device, Android 17, Ubuntu 26.04.1:

- si_errno tells the zygote's traps (0) from a stacked filter's.
- With the supervisor passing marked traps through, tawcroot runs under
  alr; acceptance stays PASS 169, FAIL 0, SKIP 0.
- A trap in a traced process costs about 60 us instead of 0.5 us, and
  `find /usr/share -type f` takes 6.0-6.3 s under the supervisor against
  0.35 s for tawcroot alone and 0.15-0.18 s native. The lane must detach
  from the supervisor on entry.
- uutils, codex and bash run in the tawcroot lane; uutils needed the
  PR_GET_AUXV fix, which is also why it fails under the preload lane.

ADR 0005 records the T5 status and the next steps.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…elative link path

- uutils' multicall dispatch names itself from the kernel's AT_EXECFN,
  read with a raw prctl(PR_GET_AUXV). Under the explicit loader that is
  ld.so's path, hence "unknown program 'ld-linux-aarch64.so'". glibc's
  ld.so only repoints its own copy, so getauxval() never showed it. At
  init the preload now overwrites that stack string with the guest path
  when it names the loader and the guest path fits.

- symlink/symlinkat relativized an absolute target only when the link
  path itself was absolute, so `ln -s /etc/hostname l` stored
  "/etc/hostname" and the kernel followed it into Android's /etc. The
  link's parent is now resolved by the kernel (O_PATH + /proc/self/fd),
  which covers relative paths, dirfds and "..".

Acceptance gains PRELOAD SYMLINK RELATIVE LINKPATH and DOTDOT LINKPATH.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
With the preload fix uutils runs under alr: acceptance PASS 171 FAIL 0
SKIP 0 and breadth 96/96 on 26.04.1 with uutils in place. install and
adopt no longer repoint /usr/bin at the gnu* binaries, and the warning
and its reason=uutils-coreutils go away. `alr use-gnu-coreutils` stays
for anyone who prefers GNU.

Two acceptance checks assumed GNU: DOCTOR P11 DYNAMIC CLEAN scans
/bin/bash instead of /bin/true (now the uutils binary, which carries its
own svc), and ALR PTY SPEED uses gnustty, since uutils' stty has no
`speed` operand and the check is about the preload's speed_t handling.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
tests/device/t2_native.sh runs git status over 10k files, node -e 0,
python3 -c pass, npm ci with 74 deps and an apt reinstall natively and
under alr with hyperfine, both as one `alr run` and as 20 runs from a
guest shell, and reports medians against ADR 0005's 1.2x budget. apt is
reported but not judged: Ubuntu's lists and dpkg database are far
larger than Termux's.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Evidence for the uutils fix and the T2 run (git status 1.08, python3
1.11, node 0.41, npm ci 0.79 against Termux), with raw acceptance and
breadth output. ADR 0006 gains an addendum: uutils lost only its name,
and the ADR now covers binaries that issue path syscalls without libc.
00-product, RISKS, 07-acceptance, the playbook, the provisioning
reason list, ADR 0005 and the product requirements are updated to
match.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
alr's preload lane and the tawcroot lane share one rootfs, so under
TAWCROOT_ALR the lane writes it the way the preload does and keeps
syscalls off the trap path:

- real uid/gid unless --fakeroot; stat not decorated root-owned
- absolute symlink targets stored relative to the link's parent
- no link store; a refused hardlink is copied
- rescue slot claimed only for virtual euid 0
- resolver caches directory prefixes known not to be symlinks,
  cleared after rename/unlink/symlink/chroot
- usercopy addresses the process by a tgid cached in a
  MADV_WIPEONFORK page
- stat-family results written by the kernel into the guest buffer

Without the flag the tree builds upstream's behaviour. VENDORING.md
lists the changes as local changes 3 to 9.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- alr run and the preload's execve hand static ELFs and ELFs with a Go
  note to alr-lane with guest arguments and a loader-free environment;
  the supervisor detaches a tracee that execs alr-lane
- Go notes are matched as cmd/link writes them ("Go\0\0", namesz 4)
- the rootfs path is canonicalized: getcwd, /proc/self/fd and
  AT_EXECFN come back canonical and never matched a root spelled
  with "." or ".."
- alr is linked static at API 29; resolvd asks netd's dnsproxyd
  directly because a static libc cannot reach netd
- alr-lane is built by make lane, the release script, the Termux
  recipe and dev-push, and ships with its MIT notice
- acceptance gains the lane and cross-lane checks; t2_native gains
  Go rows

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
ADR 0010 supersedes ADR 0006 and 0008. Evidence: acceptance 187/0/1/0,
breadth 96/96 and every T2 workload within 1.2x with the release build.
Workloads that are nothing but path syscalls stay above budget in the
lane (find 1.34x, ls -lR 1.65x).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Under -DTAWCROOT_ALR only (VENDORING.md local changes 10 to 14):

- Read guest memory with load/store loops instead of process_vm_readv.
  A bad pointer faults into alr-lane's own SIGSEGV/SIGBUS entry and
  becomes EFAULT. The guest's dispositions live in a shadow; guest
  faults are forwarded to the guest handler, SIG_DFL re-faults with the
  default action, SA_RESETHAND and SIG_IGN across exec behave as before,
  and a CLONE_VM child keeps its own settings.
- Pass a one-name relative path to the kernel with the guest's dirfd
  when the directory is on the rootfs and the name is neither a bind at
  the root nor a symlink. Cache the cwd's guest path per process.
- Stat with following looks at the leaf without following first, and
  translates symlink leaves through a cache checked by inode and ctime.
- Trap fstat only for reserved fds, and getdents64 only for fds 900-999,
  where opened /proc/<pid>/fd directories are placed.
- Let the directory cache hold following leaves too.

busybox find /usr/share: 1.28x -> 1.08x of ALR_LANE=0; ls -lR 1.33x -> 1.09x.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…n error

probe_lane.c is built static in the guest and checks 31 lane behaviours:
absolute symlinks via dirfd and cwd, vfork chdir, bind names at the
root, hidden own fds, retargeted links, EFAULT on bad pointers, and the
guest's SIGSEGV/SIGBUS handlers. acceptance.sh emits its lines.

alr-lane's handler runs on the guest's stack, so every lane build now
fails on a frame above 1024 bytes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Evidence 2026-10-11-t5-lane-cost with raw microbench, interleaved A/B,
T2 and acceptance output. ADR 0005 T5 is met; VENDORING records local
changes 10 to 15; RISKS lists the guest-visible differences.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A fork child whose first trapped call came from its own vfork child
cached that child's pid as the memory owner. The vfork child then reset
SIGSEGV in the main shadow and the fork child lost its handler (new
probe check reproduced it 5/5 before this change).

Under -DTAWCROOT_ALR, clone without CLONE_VM now traps. The handler runs
the clone and the child stores its tgid before returning to guest code;
a new child stack is applied to the child's signal context. Threads and
vfork still do not trap.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…rball

A release unpacked outside $PREFIX did not find its own
libexec/alr/alr-lane, share/alr/libalr_preload.so or libdl stub: alr
looked only beside itself and in $PREFIX. It now also looks in
<bin>/../libexec/alr and <bin>/../share/alr.

bsdtar on macOS stored com.apple.provenance as ._<name> members, which
Termux's tar extracted as files. make-release.sh now archives without
extended attributes.

acceptance.sh finds alr-doctor beside $ALR, so an unpacked release runs
the doctor checks instead of skipping them.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
An opened /proc/<pid>/fd directory moved to fd 900-999 so that only that
band trapped getdents64. Now the directory keeps the fd the kernel gave
it and gets a small stacked filter (TSYNC) trapping getdents64 on exactly
that number. Other directories still read without a trap; find/ls cost
is unchanged against the band. The band remains the fallback when no
filter can be added (more than 32 numbers, or a CLONE_VM child).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Meapri and others added 6 commits October 11, 2026 23:30
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…1228 ns)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Rewrite runtime/alr/docs/INSTALL.md for Ubuntu 26.04 and the tawcroot
lane: building the tarball from the repo, the published v0.5.0 release
(no alr-lane, 24.04 default) and how to tell them apart, unpacking
outside $PREFIX, --fakeroot for apt, lane limits and troubleshooting.
Link it from the top-level README.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Copilot AI balanced review requested due to automatic review settings October 11, 2026 15:49

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 11, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-10-11T16:01:49.075579Z ae3493c PR opened
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@Meapri
Meapri merged commit cbbea26 into main Oct 11, 2026
0 of 2 checks passed

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: ae3493c3d8

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

-static -nostdlib -nostartfiles -Wl,--image-base=0x10000000 \
-Wl,-z,max-page-size=16384 -Wl,-z,common-page-size=16384 \
-o alr-lane \
third_party/tawcroot/src/*.c third_party/tawcroot/src/arch/aarch64_*.S

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Point the recipe at sources containing alr-lane

The recipe still downloads the pinned v0.5.0 source archive, while the newly added build command expects third_party/tawcroot; runtime/alr/docs/INSTALL.md explicitly documents that the published v0.5.0 predates alr-lane. Consequently a standard Termux package build receives sources without these files and fails when this glob is compiled. Bump the package version/source hash or add a separate source containing the vendored lane.

Useful? React with 👍 / 👎.

# whether this recipe's $TERMUX_PKG_API_LEVEL and default flags accept
# -static is UNVERIFIED against termux-packages -- if not, drop it and
# the package is only slower.
$CC $CPPFLAGS $CFLAGS $LDFLAGS -O1 -static \

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Compile the static Termux binary against API 29

In a standard Termux package build, TERMUX_PKG_API_LEVEL defaults to 24 because this recipe never overrides it (Termux build setup). Adding -static therefore links alr with the API-24 libc.a, even though this repository's Makefile and device evidence state that this exact combination aborts at startup with an underaligned TLS segment and deliberately target Android API 29. Set the package API to 29 or invoke a separate API-29 compiler for alr while retaining the desired doctor target.

Useful? React with 👍 / 👎.

Comment thread runtime/alr/src/cli/alr.c
Comment on lines +3342 to +3344
if (!strncmp(ge[k], "LD_", 3) || !strncmp(ge[k], "ALR_", 4)
|| !strncmp(ge[k], "LOCPATH=", 8))
continue;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Preserve caller-owned variables when entering the lane

For any top-level static or Go target, this prefix filter silently drops caller-owned variables such as ALR_GPU_BRIDGE_HOST or any accepted -e ALR_CUSTOM=value, despite env_is_reserved() deliberately reserving only the runtime's enumerated names so embedders can use the rest of the ALR_ namespace. The analogous env_strip() path also drops every ALR_ variable for guest-initiated lane transitions. Strip only the runtime-owned keys rather than the entire prefix.

Useful? React with 👍 / 👎.

Comment on lines +734 to +738
long rv = TAWC_RAW(TAWC_SYS_ioctl, fd, args->b, arg,
args->d, args->e, args->f);
if (rv == TAWC_EACCES && is_netdev_get_ioctl(cmd))
return netdev_ioctl_on_inet(cmd, arg);
return rv;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Emulate Android-denied PTY ioctls inside the lane

When a static or Go program enters alr-lane, all of its descendants remain there without the preload wrappers. Android's measured policy denies TIOCGSID, TIOCGETD, and TIOCEXCL, but this lane handler only translates termios2 and netdevice requests before returning the raw EACCES. Consequently PTY consumers launched from a static shell or Codex can fail even though the same ioctls are explicitly emulated on the normal preload path; the acceptance ioctl probe currently exercises only that preload path. Mirror those PTY translations in the lane handler.

Useful? React with 👍 / 👎.

Comment on lines +656 to +659
unsigned char zero[TAWC_KERN_TERMIOS2_TAIL] = {0};
e = tawc_copy_to_guest((void *)(uintptr_t)
(arg + TAWC_KERN_TERMIOS_SIZE),
zero, sizeof zero);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Preserve termios2 speeds in the lane fallback

On devices where Android rejects TCGETS2, every dynamic Ubuntu 26.04 descendant running inside the lane uses this fallback. Glibc 2.43 interprets the trailing fields as actual baud rates, so zeroing them reports speed 0 rather than the PTY's CBAUD-derived rate; the corresponding setter also discards requested speed fields. The preload path added explicit code-to-baud conversion for this same ABI, but lane-launched tools such as gnustty still receive incorrect terminal state. Populate and consume the termios2 speed fields when converting to or from legacy TCGETS/TCSETS.

Useful? React with 👍 / 👎.

Comment thread runtime/alr/src/cli/alr.c
/* the lane's ids follow the preload's: real unless fakeroot */
if (fake) av[n++] = (char *)"--fakeroot";
av[n++] = (char *)"--";
av[n++] = (char *)guest_cmd;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Pass the PATH-resolved command to alr-lane

When the user supplies a command without a slash, resolve() searches the guest PATH and records the located binary in host, but this lane invocation passes the original bare guest_cmd. Unlike execvp, alr-lane opens its command path directly and performs no PATH search, so common forms such as alr run codex, alr run go version, or alr run busybox fail after being successfully classified for the lane; the current lane tests avoid this by using absolute paths. Pass the resolved guest path as the lane executable while preserving the requested value separately as argv[0] if needed.

Useful? React with 👍 / 👎.

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.

3 participants