[Bug stdio/34335] New: fseek() deferred and ftell() wrong after fread() on character device whose .read does not advance *ppos

twied at gmx dot net via Glibc-bugs <[email protected]> Tue, 30 Jun 2026 14:56:14 +0000
Newsgroups gmane.comp.lib.glibc.bugs
Message-ID <[email protected]/bugzilla/>
https://sourceware.org/bugzilla/show_bug.cgi?id=34335

            Bug ID: 34335
           Summary: fseek() deferred and ftell() wrong after fread() on
                    character device whose .read does not advance *ppos
           Product: glibc
           Version: unspecified
            Status: UNCONFIRMED
          Severity: normal
          Priority: P2
         Component: stdio
          Assignee: unassigned at sourceware dot org
          Reporter: twied at gmx dot net
  Target Milestone: ---

After fseek() + fread() on /dev/cpu/*/msr, both ftell() and lseek(fileno(f), 0,
SEEK_CUR) return wrong values. The two also disagree with each other.

The MSR driver's .read handler does not advance the kernel file position
(*ppos). This is unusual (/dev/cpu/*/cpuid, /dev/port, and /dev/nvram do
advance the file file position), but intentional.

Using raw file descriptor operations, I get the correct result:
```
int fd = open("/dev/cpu/0/msr", O_RDONLY);
lseek(fd, 0x10, SEEK_SET);
read(fd, &val, 8);
lseek(fd, 0, SEEK_CUR);         // returns 0x10
```

Using stdio:
```
FILE *f = fopen("/dev/cpu/0/msr", "r");
fseek(f, 0x10, SEEK_SET);
fread(&val, 8, 1, f);
ftell(f);                       // returns 0x18
lseek(fileno(f), 0, SEEK_CUR);  // returns 0x0
```

I am aware that calling ftell() or lseek() on a character device is unusual,
and that file position may be meaningless for some devices (serial ports,
/dev/null, etc.). The MSR driver, however, uses the file position as a register
index and supports lseek, by design.

Related LKML discussion on the MSR driver's *ppos behavior:
https://lore.kernel.org/lkml/[email protected]/

Full reproducer (needs x86 and root):

```
#include <fcntl.h>
#include <stdint.h>
#include <stdio.h>
#include <unistd.h>

int main(void)
{
    uint64_t val;

    /* Raw fd: driver behaves correctly */
    int fd = open("/dev/cpu/0/msr", O_RDONLY);
    if (fd < 0) {
        perror("/dev/cpu/0/msr");
        return 1;
    }

    off_t pos_seek = lseek(fd, 0x10, SEEK_SET);
    ssize_t n = read(fd, &val, sizeof(val));
    if (n != sizeof(val)) {
        perror("read");
        return 1;
    }
    off_t pos_after = lseek(fd, 0, SEEK_CUR);

    printf("Raw fd:\n");
    printf("  after lseek(0x10): 0x%lx\n", (long)pos_seek);
    printf("  after read():      0x%lx\n", (long)pos_after);

    close(fd);

    /* stdio: both ftell and lseek return wrong values */
    FILE *f = fopen("/dev/cpu/0/msr", "r");
    if (!f) {
        perror("/dev/cpu/0/msr");
        return 1;
    }

    if (fseek(f, 0x10, SEEK_SET) != 0) {
        perror("fseek");
        return 1;
    }

    if (fread(&val, sizeof(val), 1, f) != 1) {
        perror("fread");
        return 1;
    }

    long pos_ftell = ftell(f);
    off_t pos_lseek = lseek(fileno(f), 0, SEEK_CUR);

    printf("stdio:\n");
    printf("  ftell():           0x%lx\n", pos_ftell);
    printf("  lseek(SEEK_CUR):   0x%lx\n", (long)pos_lseek);
    printf("  expected:          0x10\n");

    fclose(f);
    return 0;
}
```

Tested on glibc 2.39, kernel 6.17.10 Fedora 43 and glibc 2.42, kernel 6.19.8
Debian 14.

-- 
You are receiving this mail because:
You are on the CC list for the bug.