apple / apple/container

[Bug]: signal.alarm() returns corrupted (sign-flipped) value in x86_64 Linux guest running under Rosetta translation

Open
#2,255 1 comment 0 reactions 0 assignees View on GitHub
Dominant language
Swift
Stars
49.9k
Forks
1.8k
Avg merge
1d 20h
Merged PRs (30d)
22

Description

### I have done the following

- [x] I have searched the existing issues
- [ ] If possible, I've reproduced the issue using the 'main' branch of this project

### Steps to reproduce

`container build --platform linux/amd64 -t rosetta-alarm-repro`
`container run --platform linux/amd64 --rm rosetta-alarm-repro`
```
### Containerfile
# Minimal, standalone reproduction of a signal.alarm() integer-corruption bug
# seen under Apple's `container` CLI on Apple Silicon (Rosetta 2 x86_64
# translation).
#
# Two bare signal.alarm() calls, no forking, no
# multiprocessing. On an affected host the second call returns a corrupted
# value; on a correctly-functioning host it prints a value close to
# expected_remaining.
#
# Not expected to reproduce on native amd64 hardware/VMs - it is specific to
# Rosetta 2 binary translation of x86_64 guests on Apple Silicon.
#
# Build (must target amd64 so Apple's `container` CLI runs it under Rosetta):
# container build --platform linux/amd64 -t rosetta-alarm-repro .
# Run (--platform is required again here - `container run` doesn't infer it
# from the image and defaults to the host's native arm64 otherwise):
# container run --platform linux/amd64 --rm rosetta-alarm-repro

FROM python:3.12-slim
COPY alarm_probe.py /alarm_probe.py
CMD ["python3", "/alarm_probe.py"]
```
### `alarm_probe.py`

[alarm_probe.py](https://github.com/user-attachments/files/32060754/alarm_probe.py)

### Problem description

## Current behavior

signal.alarm() returns a corrupted value when called a second time on an armed alarm, inside an
x86_64 Linux guest run via `container run --platform linux/amd64` on Apple Silicon.

Concretely: arm an alarm for abort_after=10800 seconds, sleep ~16s, then call
signal.alarm(abort_after) again (the standard idiom for querying "seconds remaining" while
re-arming). The return value should be the number of seconds left on the previous alarm
(~10784), but instead comes back as 4294956512.

4294956512 == 2**32 - 10784 — i.e. the correct remaining time, sign-flipped and reinterpreted
as an unsigned 32-bit integer.

Passing that corrupted value into a subsequent signal.alarm() call (which is exactly what real
code does when reusing the "seconds left" return value to re-arm) raises:

OverflowError: Python int too large to convert to C int

(Under Python 2.7 the same corrupted value instead raises OverflowError: signed integer is
greater than maximum.)

## Expected behavior

Per the POSIX/glibc alarm() contract, calling alarm() on an already-armed alarm should return
the number of seconds remaining on the previous alarm — a value in [0, abort_after] for
abort_after seconds previously armed. It should never return a value outside that range, and
never a value that doesn't fit back into alarm()'s own C long argument.

Confirmed this reproduces identically under both Python 3.12.14 and Python 2.7.18 with nothing
but two bare signal.alarm() calls — no forking, multiprocessing, or third-party code involved —
which points to the corruption happening in the raw alarm() syscall return value itself (i.e.,
in the Rosetta 2 x86_64→arm64 translation), not in CPython.

## Relevant logs

Minimal repro output on the affected host (see attached Containerfile):
```
elapsed=16.006 expected_remaining~=10784 actual_return=4294956512 as_signed32=-10784

Second variant (alarm_probe.py, no sleep, two back-to-back calls):

re-arm alarm() return (should be ~3600): 4294967295
OverflowError: Python int too large to convert to C int
```
Host environment: macOS 26.6.2 (build 25G83), Command Line Tools 26.6.0.0.1781586589, Rosetta
1.0.0.0.1786586848; guest is Debian trixie, glibc 2.41, systemd-detect-virt reports
container-other.

### Environment

```markdown
- macOS: 26.6.2 (build 25G83)
- Command Line Tools: 26.6.0.0.1781586589
- Rosetta: 1.0.0.0.1786586848
- Xcode.app: not installed (CLT-only xcode-select path), so no xcodebuild -version to report
- Container: container CLI version 1.3.1 (build: release, commit: unspeci)
```

### Code of Conduct

- [x] I agree to follow this project's Code of Conduct

Contributor guide

Open the contributing guide

Research direction

Start by building and running the attached Containerfile and alarm_probe.py with the documented amd64 platform commands. Compare signal.alarm() behavior in the reported x86_64 Linux guest with native or non-Rosetta execution, then determine whether the container project can address the corrupted return value or needs an upstream Rosetta report; done means the repro is explained and the expected alarm value no longer overflows.

Written by the indexing model from the issue text.

Assessment

Tech stack
linux, python, swift
Domain
infrastructure, operating-systems
Issue type
Bug
Difficulty
5/5
Estimated time
Over a week
Activity status
Active
Clarity
Needs clarification
Newbie friendliness
25/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.