Patch | Description | Author | Forwarded | Bugs | Origin | Last update |
---|---|---|---|---|---|---|
0001-uncompress-wait-until-the-child-as-exited-to-close-t.patch | uncompress: close the pipe after the child exits (LP: #2008508) Since we sometimes interrupt the decompression mid-stream as we're only looking for specific data, e.g. the control file in control.tar.zstd, it can happen that the child process still has a backlog of data to write out to the pipes before exiting. If we close its stdout pipe before calling waitpid(), it's going to encounter an EPIPE rather than gracefully exit. The fix is to only close the pipe fd after waitpid() successfully exits. However, that introduces a new theoretical issue: the child process could be blocking while writing into its stdout if the pipe is full, thus leading to a deadlock. To avoid this, we have to drain the pipe before waiting. Technically this should probably be done in a loop, but since it's fairly unlikely to be blocked on stdout in the first place, having enough pending data to fill the pipe *twice* seems too rare to bother with in the first place. The initial problem has first been noticed in Ubuntu autopkgtests on s390x when upgrading to libarchive 3.6.2, where unzstd would loudly complain about an -EPIPE (Ubuntu is using zstd as its default compression algorithm). After investigation, it could be consistently reproduced on low-powered s390x VMs with older libarchive releases, where only the initial invocation would succeed, but subsequent attempts would fail, presumably due performance changes via caching? To reproduce the issue more reliably, I used the following Rust code to produce a "proxy" unzstd that I dropped in /usr/local/bin. The output is the same, but only written 1kB at a time with 500ms pauses. ```rust use std::io::{Read, Write}; use std::process::{Command, Stdio}; fn main() { let mut child = Command::new("zstd") .arg("-d") .stdin(Stdio::inherit()) .stdout(Stdio::piped()) .spawn() .expect("Failed to spawn the command"); let mut stdout = child.stdout.take().unwrap(); let mut buffer = [0u8; 1024]; loop { std::thread::sleep(std::time::Duration::from_millis(500)); let size = stdout.read(&mut buffer).unwrap(); if size > 0 { std::io::stdout().write_all(&buffer[0..size]).unwrap(); std::io::stdout().flush().unwrap(); continue; } if let Some(status) = child.try_wait().unwrap() { std::process::exit(status.code().unwrap()) } } } ``` (to compile it: `rustc unzstd.rs`) Due to the Debian freeze, we'll probably ship this patch in Ubuntu as a delta. |
Simon Chopin <schopin@ubuntu.com> | no | upstream, 6a8ac062c49093ad33689754558e626851070e61 | 2023-03-01 | |
0002-uncompress-prevent-reprepro-from-hanging-on-unzstd.patch | Wait for poll event | Bastian Germann <bage@debian.org> | no | 2024-01-08 |