geom_linux_lvm: Check the offset of physical volume header

The LVM label is 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
Reviewed by:	jhb
MFC after:	2 weeks
Differential Revision:	https://reviews.freebsd.org/D36773
This commit is contained in:
Zhenlei Huang 2023-08-22 17:20:10 +08:00
parent 4d004ccce2
commit c941b82e1c

View file

@ -67,7 +67,8 @@ static int g_llvm_read_label(struct g_consumer *, struct g_llvm_label *);
static int g_llvm_read_md(struct g_consumer *, struct g_llvm_metadata *,
struct g_llvm_label *);
static int llvm_label_decode(const u_char *, struct g_llvm_label *, int);
static int llvm_label_decode(const u_char *, struct g_llvm_label *,
int, u_int);
static int llvm_md_decode(const u_char *, struct g_llvm_metadata *,
struct g_llvm_label *);
static int llvm_textconf_decode(u_char *, int,
@ -637,7 +638,8 @@ g_llvm_read_label(struct g_consumer *cp, struct g_llvm_label *ll)
/* Search the four sectors for the LVM label. */
for (i = 0; i < 4; i++) {
error = llvm_label_decode(&buf[i * pp->sectorsize], ll, i);
error = llvm_label_decode(&buf[i * pp->sectorsize], ll, i,
pp->sectorsize);
if (error == 0)
break; /* found it */
}
@ -703,7 +705,8 @@ g_llvm_read_md(struct g_consumer *cp, struct g_llvm_metadata *md,
}
static int
llvm_label_decode(const u_char *data, struct g_llvm_label *ll, int sector)
llvm_label_decode(const u_char *data, struct g_llvm_label *ll, int sector,
u_int sectorsize)
{
uint64_t off;
char *uuid;
@ -728,6 +731,13 @@ llvm_label_decode(const u_char *data, struct g_llvm_label *ll, int sector)
return (EINVAL);
}
/* XXX The minimal possible size of physical volume header is 88 */
if (ll->ll_offset < 32 || ll->ll_offset > sectorsize - 88) {
G_LLVM_DEBUG(0, "Invalid physical volume header offset %u",
ll->ll_offset);
return (EINVAL);
}
off = ll->ll_offset;
/*
* convert the binary uuid to string format, the format is