Skip to content

Read corruption after O_TRUNC + file_copy_range (under high IO load)Β #18412

Closed

Description

@nbdd0121

System information

Type Version/Name
Distribution Name NixOS
Distribution Version NixOS 25.11 (Xantusia)
Kernel Version 6.18.19
Architecture x86_64
OpenZFS Version 2.4.1

Describe the problem you're observing

I have a program which reads a file after a copy (copy at https://github.com/rust-lang/rust/blob/ad4b9354009cb6bd5a9ff1b5f5a63a13ec98ebc9/compiler/rustc_codegen_ssa/src/back/write.rs#L524, read at https://github.com/Rust-for-Linux/klint/blob/62f35fa7fc978aee288a8cc859e0cc40d9c32e56/src/binary_analysis/mod.rs#L22, but the read in theory could also be linker or another dependent compilation).

This works fine for unit testing the program, however when I build kernel with -j64 this causes issue. The file read can sometimes read back corrupted file. I've investigated the corrupted read, and I found that the first 128KiB are all zero, and this matches my recordsize, so I suspect ZFS is causing the bug.

Clean build never triggers the issue, and the issue only triggers when source is modified and rebuilt with target .o file already present. Also interestingly, the file itself is not corrupted, if you read the content afterwards. Only the read is.

Describe how to reproduce the problem

I've minimized the reproduction, and this has fairly reliably reproduce the bug on my machine:

#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <pthread.h>
#include <errno.h>

#define FILE_SIZE (1024 * 1024) // 1MB
#define RECORD_SIZE (128 * 1024) // 128KiB ZFS recordsize
#define NUM_THREADS 64

void *run_test(void *arg)
{
	int thread_id = (int)(long)arg;

	char src_path[64], dst_path[64];
	sprintf(src_path, "src-%d.bin", thread_id);
	sprintf(dst_path, "dst-%d.bin", thread_id);

	unsigned char *write_buf = malloc(FILE_SIZE);
	unsigned char *read_buf = malloc(FILE_SIZE);

	// Write out expected data.
	memset(write_buf, 0xAA, FILE_SIZE);
	int src = open(src_path, O_WRONLY | O_CREAT | O_TRUNC, 0644);
	if (write(src, write_buf, FILE_SIZE) != FILE_SIZE)
		perror("write");
	close(src);

	// Create destination file so we exercise O_TRUNC.
	int dst = open(dst_path, O_WRONLY | O_CREAT | O_TRUNC, 0644);
	if (write(dst, write_buf, FILE_SIZE) != FILE_SIZE)
		perror("write");
	fsync(dst);
	close(dst);

	// Open file with O_TRUNC and perform copy.
	src = open(src_path, O_RDONLY);
	dst = open(dst_path, O_WRONLY | O_CREAT | O_TRUNC, 0644);

	loff_t off_in = 0, off_out = 0;
	ssize_t ret =
		copy_file_range(src, &off_in, dst, &off_out, FILE_SIZE, 0);
	if (ret != FILE_SIZE)
		perror("copy_file_range");
	close(src);
	close(dst);

	// Read back
	dst = open(dst_path, O_RDONLY);
	if (read(dst, read_buf, FILE_SIZE) != FILE_SIZE)
		perror("read");
	close(dst);

	// Bug check
	if (memcmp(write_buf, read_buf, FILE_SIZE) != 0) {
		fprintf(stderr, "[%d]: FAIL\n", thread_id);

		int all_zeros = 1;
		for (int i = 0; i < RECORD_SIZE; i++) {
			if (read_buf[i] != 0) {
				all_zeros = 0;
				break;
			}
		}

		if (all_zeros) {
			fprintf(stderr, "[%d]: ALL ZERO\n", thread_id);
		}

		goto keep_file;
	}

	unlink(src_path);
	unlink(dst_path);
keep_file:
	free(write_buf);
	free(read_buf);
	return NULL;
}

int main()
{
	pthread_t threads[NUM_THREADS];

	for (int i = 0; i < NUM_THREADS; i++) {
		pthread_create(&threads[i], NULL, run_test, (void *)(long)i);
	}
	for (int i = 0; i < NUM_THREADS; i++) {
		pthread_join(threads[i], NULL);
	}

	return 0;
}

An example failing run gives:

[20]: FAIL
[4]: FAIL
[20]: ALL ZERO
[4]: ALL ZERO
[14]: FAIL
[15]: FAIL
[14]: ALL ZERO
[22]: FAIL
[22]: ALL ZERO
[15]: ALL ZERO
[16]: FAIL
[21]: FAIL
[16]: ALL ZERO
[21]: ALL ZERO

On other file system, this works cleanly without the issue.

Activity

  1. added a commit that references this issue on Apr 15, 2026
    1644e2f
  2. added a commit that references this issue on Apr 23, 2026
    83c04e5
  3. added a commit that references this issue on Apr 23, 2026
    9d08a92
  4. added 2 commits that reference this issue on May 6, 2026
    dceca0d
    e752459
  5. added a commit that references this issue on Jul 30, 2026
    2b8aeeb
  6. added a commit that references this issue on Sep 26, 2026
    8bcae43
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type: DefectIncorrect behavior (e.g. crash, hang)

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions