Page MenuHomeFreeBSD

geom_linux_lvm: Check the offset of physical volume header
ClosedPublic

Authored by zlei on Sep 28 2022, 2:22 PM.
Tags
None
Referenced Files
Unknown Object (File)
Mon, Oct 21, 1:05 AM
Unknown Object (File)
Mon, Oct 21, 1:05 AM
Unknown Object (File)
Mon, Oct 21, 1:05 AM
Unknown Object (File)
Mon, Oct 21, 1:05 AM
Unknown Object (File)
Mon, Oct 21, 1:05 AM
Unknown Object (File)
Fri, Oct 18, 7:29 PM
Unknown Object (File)
Oct 4 2024, 3:21 PM
Unknown Object (File)
Sep 24 2024, 11:16 PM
Subscribers

Details

Summary

The LVM label are stored on any of the first four sectors, and the
PV (physical volume) header is stored within the same sector following
the LVM label. The current implementation does not fully check the
offset of PV header, when attaching a bad formatted LVM PV the kernel
may crash due to out-of-bounds memory read.

PR: 266562
MFC after: 2 weeks

Diff Detail

Repository
rG FreeBSD src repository
Lint
Lint Not Applicable
Unit
Tests Not Applicable

Event Timeline

zlei requested review of this revision.Sep 28 2022, 2:22 PM

It would be a lot easier to understand this I think if we had a couple of structs. It would also make this code easier to read:

struct lvm_label_header {
    char sig[8];
    uint64_t sector;
    uint32_t crc;
    uint32_t offset;
    char format[8];
};

/* Starts at `offset` field in label sector. */
struct lvm_label_data {
    char uuid[32];
    uint64_t size;
    uint64_t pestart;
    uint64_t reserved1;
    uint64_t sections;
    uint64_t reserved2;
    uint64_t md_offset;
    uint64_t md_size;
};

Then you could write your expression as if (ll->ll_offset < sizeof(struct lvm_label_header) || ll->ll_offset >= sectorsize - sizeof(struct lvm_label_data)) which would be easier to understand.

It would also permit rewriting much of this function to avoid all the weird off arithmetic (apart from inserting the hyphens in the UUID string).

This revision is now accepted and ready to land.Aug 4 2023, 11:57 PM

This is a WIP fix for https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=266562 , I think I should do some refactor as @jhb suggested so it will be much easier to read.

sys/geom/linux_lvm/g_linux_lvm.c
734

I should leave some comments about the hard coded offsets. ;)

Rebased to latest main branch.

This revision now requires review to proceed.Aug 18 2023, 8:27 AM
zlei retitled this revision from geom_linux_lvm: Check data offset in physical volume label header to geom_linux_lvm: Check offset of physical volume header in label header.
  1. Fixed off-by-one.
  2. Added comment about the hard coded number.
zlei retitled this revision from geom_linux_lvm: Check offset of physical volume header in label header to geom_linux_lvm: Check the offset of physical volume header.
In D36773#941067, @jhb wrote:

It would be a lot easier to understand this I think if we had a couple of structs. It would also make this code easier to read:

struct lvm_label_header {
    char sig[8];
    uint64_t sector;
    uint32_t crc;
    uint32_t offset;
    char format[8];
};

/* Starts at `offset` field in label sector. */
struct lvm_label_data {
    char uuid[32];
    uint64_t size;
    uint64_t pestart;
    uint64_t reserved1;
    uint64_t sections;
    uint64_t reserved2;
    uint64_t md_offset;
    uint64_t md_size;
};

Then you could write your expression as if (ll->ll_offset < sizeof(struct lvm_label_header) || ll->ll_offset >= sectorsize - sizeof(struct lvm_label_data)) which would be easier to understand.

It would also permit rewriting much of this function to avoid all the weird off arithmetic (apart from inserting the hyphens in the UUID string).

For the struct lvm_label_data the actual on-disk layout is not that simple, I'd commit this without refactoring.

This revision was not accepted when it landed; it landed in state Needs Review.Aug 22 2023, 9:39 AM
This revision was automatically updated to reflect the committed changes.