Luigit
repositories / termux-janitor

termux-janitor

Interactive cleanup assistant for Termux: transparent, safe, confirmed disk reclamation.

owned by admin

spec/experiments/descendant_ancestry_race.md

Raw
Rendered preview

Descendant ancestry revalidation experiment

Question

Does revalidating a reviewed descendant through a retained parent descriptor establish that the descendant remains below the selected directory?

This tests recursive-deletion containment in PRODUCT.md.

Environment

  • Date: 2026-09-13
  • Kernel: Linux 6.1.176 Android 14, AArch64
  • Compiler: Clang 21.1.8 targeting AArch64 Android
  • Fixture: a fresh private directory below Termux $TMPDIR

Method

A native C probe created selected/sub/item, opened descriptors for selected and sub, and recorded the item's device and inode. It then renamed sub to moved-outside, beside selected. After the move it revalidated and deleted item through the retained sub descriptor. It also attempted to reopen sub/item relative to the selected-root descriptor.

Observations

retained-parent revalidation: identity_match=yes
selected-root lookup after move: result=failed errno=2
retained-parent unlink: result=success moved_outside_item_survives=no

The descendant identity remained unchanged, so leaf identity checks through the retained parent did not detect the ancestry change. A lookup anchored at the selected root did detect that the reviewed path was no longer below that root. The retained descriptor nevertheless authorized deletion at the moved location.

Conclusion

Leaf identity revalidation does not establish current ancestry. Recursive execution must re-establish the reviewed ancestor chain from the selected-root identity and block every observed mismatch. A retained descendant descriptor is not sufficient containment evidence by itself.

There remains a non-atomic interval after the final ancestry check in which a same-permission process can move an ancestor. The specification must disclose this alongside same-name replacement rather than claim unconditional containment under adversarial concurrent rename. The probe does not show escape through symlink traversal and does not widen the reviewed object set; it shows that a reviewed object can be deleted after moving outside the reviewed location.

# Descendant ancestry revalidation experiment

## Question

Does revalidating a reviewed descendant through a retained parent descriptor establish that the descendant remains below the selected directory?

This tests recursive-deletion containment in [`PRODUCT.md`](../PRODUCT.md#111-filesystem-revalidation).

## Environment

- Date: 2026-09-13
- Kernel: Linux 6.1.176 Android 14, AArch64
- Compiler: Clang 21.1.8 targeting AArch64 Android
- Fixture: a fresh private directory below Termux `$TMPDIR`

## Method

A native C probe created `selected/sub/item`, opened descriptors for `selected` and `sub`, and recorded the item's device and inode.
It then renamed `sub` to `moved-outside`, beside `selected`.
After the move it revalidated and deleted `item` through the retained `sub` descriptor.
It also attempted to reopen `sub/item` relative to the selected-root descriptor.

## Observations

```text
retained-parent revalidation: identity_match=yes
selected-root lookup after move: result=failed errno=2
retained-parent unlink: result=success moved_outside_item_survives=no
```

The descendant identity remained unchanged, so leaf identity checks through the retained parent did not detect the ancestry change.
A lookup anchored at the selected root did detect that the reviewed path was no longer below that root.
The retained descriptor nevertheless authorized deletion at the moved location.

## Conclusion

**Leaf identity revalidation does not establish current ancestry.**
Recursive execution must re-establish the reviewed ancestor chain from the selected-root identity and block every observed mismatch.
A retained descendant descriptor is not sufficient containment evidence by itself.

There remains a non-atomic interval after the final ancestry check in which a same-permission process can move an ancestor.
The specification must disclose this alongside same-name replacement rather than claim unconditional containment under adversarial concurrent rename.
The probe does not show escape through symlink traversal and does not widen the reviewed object set; it shows that a reviewed object can be deleted after moving outside the reviewed location.