Subject: Bug#1014839: coreutils: dd oflag=seek_bytes brokenly interacts with sub-obs= truncation

From: наб <[email-masked]>
Date: Wed, 13 Jul 2022 01:03:22 +0200

Package: coreutils
Version: 8.32-4+b1
Severity: normal

Dear Maintainer,

Consider the following:
  $ printf 'abcdAA' > file
  $ printf '_D' | dd of=file skip=1 iflag=skip_bytes seek=3 oflag=seek_bytes status=none

What do you expect file to contain?
I, very obviously, expect to:
  1. seek to position 3
  2. truncate there
  3. skip 1 byte
  4. read the rest => EOF => write it
for "abcD". But it's not: it's 00 00 00 44,
and strace reveals that file was opened with O_TRUNC.

If we repeat the same exercise but spec bs=1 (or obs=1, these are
equivalent for the purposes of this report), we get the expected value
(and see ftruncate(1, 3)).

The same happens for bs=2 and bs=3!

bs=4 yields the original "\0\0\0D" again, though.

I had expected this to be an overzealous and backward interpretation of
the standard, which reads
> If seek= expr is specified, but conv= notrunc is not, the effect of the
> copy shall be to preserve the blocks in the output file over which dd
> seeks, but no other portion of the output file shall be preserved.
but that's not the case, and coreutils appears to have extended this
logically as expected when faced with sub-block seek offsets
(the standard is specced entirely in terms of blocks,
 but if you spec seek_bytes the intent is to obviously preserve
 everything prior to the seek offset, rather than just the sought-over
 blocks ‒ this is demonstrated by bs=2 being fine,
 the counterfactual would be "ab\0D").

This makes me believe this is a simple programming error when optimising
for "pick O_TRUNC instead of ftruncate(1, 0) later", like in
  https://git.sr.ht/~nabijaczleweli/voreutils/tree/7f7fbb1d95b66e832e339324c298ff5c8721a570/item/cmd/dd.cpp#L373-378
except that it probably checks for
  !(seek_off / obs)
which is, obviously, 0 for 3/≥4.

Best,
наб

-- System Information:
Debian Release: 11.4
  APT prefers stable-updates
  APT policy: (500, 'stable-updates'), (500, 'stable-security'), (500, 'stable-debug'), (500, 'stable')
Architecture: amd64 (x86_64)
Foreign Architectures: i386

Kernel: Linux 5.10.0-15-amd64 (SMP w/24 CPU threads)
Kernel taint flags: TAINT_PROPRIETARY_MODULE, TAINT_FIRMWARE_WORKAROUND, TAINT_OOT_MODULE, TAINT_UNSIGNED_MODULE
Locale: LANG=en_GB.UTF-8, LC_CTYPE=en_GB.UTF-8 (charmap=UTF-8), LANGUAGE=en_GB:en
Shell: /bin/sh linked to /usr/bin/dash
Init: systemd (via /run/systemd/system)
LSM: AppArmor: enabled

Versions of packages coreutils depends on:
ii  libacl1      2.2.53-10
ii  libattr1     1:2.4.48-6
ii  libc6        2.31-13+deb11u3
ii  libgmp10     2:6.2.1+dfsg-1+deb11u1
ii  libselinux1  3.1-3

coreutils recommends no packages.

coreutils suggests no packages.

-- no debconf information