Re: [PATCH 05/12] libfdt: Safer access to memory reservations

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]



On Thu, Apr 05, 2018 at 12:21:02PM +1000, Alexey Kardashevskiy wrote:
> On 4/4/18 9:06 pm, David Gibson wrote:
> > On Tue, Mar 27, 2018 at 06:09:12PM +1100, Alexey Kardashevskiy wrote:
> >> On 26/3/18 10:25 am, David Gibson wrote:
> >>> fdt_num_mem_rsv() and fdt_get_mem_rsv() currently don't sanity check their
> >>> parameters, or the memory reserve section offset in the header.  That means
> >>> that on a corrupted blob they could access outside of the range of memory
> >>> that they should.
> >>>
> >>> This improves their safety checking, meaning they shouldn't access outside
> >>> the blob's bounds, even if its contents are badly corrupted.
> >>>
> >>> Signed-off-by: David Gibson <david@xxxxxxxxxxxxxxxxxxxxx>
> >>> ---
> >>>  libfdt/fdt_ro.c          | 33 ++++++++++++++++++++-----
> >>>  tests/.gitignore         |  1 +
> >>>  tests/Makefile.tests     |  2 +-
> >>>  tests/run_tests.sh       |  1 +
> >>>  tests/testdata.h         |  1 +
> >>>  tests/trees.S            | 20 +++++++++++++++
> >>>  tests/truncated_memrsv.c | 63 ++++++++++++++++++++++++++++++++++++++++++++++++
> >>>  7 files changed, 114 insertions(+), 7 deletions(-)
> >>>  create mode 100644 tests/truncated_memrsv.c
> >>>
> >>> diff --git a/libfdt/fdt_ro.c b/libfdt/fdt_ro.c
> >>> index d4cec0e..c74b962 100644
> >>> --- a/libfdt/fdt_ro.c
> >>> +++ b/libfdt/fdt_ro.c
> >>> @@ -170,21 +170,42 @@ uint32_t fdt_get_max_phandle(const void *fdt)
> >>>  	return 0;
> >>>  }
> >>>  
> >>> +static const struct fdt_reserve_entry *fdt_mem_rsv(const void *fdt, int n)
> >>> +{
> >>> +	int offset = n * sizeof(struct fdt_reserve_entry);
> >>> +	int absoffset = fdt_off_mem_rsvmap(fdt) + offset;
> >>> +
> >>> +	if (absoffset < fdt_off_mem_rsvmap(fdt))
> >>> +		return NULL;
> >>> +	if (absoffset > fdt_totalsize(fdt) - sizeof(struct fdt_reserve_entry))
> >>> +		return NULL;
> >>> +	return fdt_mem_rsv_(fdt, n);
> >>> +}
> >>> +
> >>>  int fdt_get_mem_rsv(const void *fdt, int n, uint64_t *address, uint64_t *size)
> >>>  {
> >>> +	const struct fdt_reserve_entry *re;
> >>> +
> >>>  	FDT_RO_PROBE(fdt);
> >>> -	*address = fdt64_to_cpu(fdt_mem_rsv_(fdt, n)->address);
> >>> -	*size = fdt64_to_cpu(fdt_mem_rsv_(fdt, n)->size);
> >>> +	re = fdt_mem_rsv(fdt, n);
> >>> +	if (!re)
> >>> +		return -FDT_ERR_BADOFFSET;
> >>> +
> >>> +	*address = fdt64_to_cpu(re->address);
> >>> +	*size = fdt64_to_cpu(re->size);
> >>>  	return 0;
> >>>  }
> >>>  
> >>>  int fdt_num_mem_rsv(const void *fdt)
> >>>  {
> >>> -	int i = 0;
> >>> +	int i;
> >>> +	const struct fdt_reserve_entry *re;
> >>>  
> >>> -	while (fdt64_to_cpu(fdt_mem_rsv_(fdt, i)->size) != 0)
> >>> -		i++;
> >>> -	return i;
> >>> +	for (i = 0; (re = fdt_mem_rsv(fdt, i)) != NULL; i++) {
> >>> +		if (fdt64_to_cpu(re->size) == 0)
> >>> +			return i;
> >>> +	}
> >>> +	return -FDT_ERR_TRUNCATED;
> >>
> >>
> >> QEMU's spapr_h_cas_compose_response() crashes on this one as it calls
> >> fdt_open_into() and that guy does not check if fdt_num_mem_rsv() fails and
> >> it does as there is no any reserved entry, even an empty one in the end of
> >> the list.
> >>
> >> fdt_create() could create an empty record or fdt_open_into() could test for
> >> an error, not sure which one is right here.
> > 
> > Ah.  So, technically spapr_h_cas_compose_response() is just doing the
> > wrong thing here.  You're supposed to construct a mem reserve segment
> > between fdt_create() and the first fdt_begin_node(), even if that's
> > just a single call to fdt_finish_reservemap().
> 
> Fair enough, I've posted a patch for QEMU.
> 
> > That said, it would be good for libfdt to be more robust against
> > that.  Either fdt_begin_node() should return FDT_ERR_BADSTATE in this
> > situatipon, or it should automatically finish off the reserve map.
> 
> Returning an error from fdt_begin_node() should do it. Also probably
> including fdt_finish() as it looks like it is possible to call fdt_create()
> and fdt_finish() without creating nodes in between (have not tested this).
> 
> Automation is supposed to fix only one place in QEMU for now and it just
> does not seem worth it.

Fair enough.  I've fixed libfdt to return BADSTATE if you try to begin
a node before completing the reservemap.

-- 
David Gibson			| I'll have my music baroque, and my code
david AT gibson.dropbear.id.au	| minimalist, thank you.  NOT _the_ _other_
				| _way_ _around_!
http://www.ozlabs.org/~dgibson

Attachment: signature.asc
Description: PGP signature


[Index of Archives]     [Device Tree]     [Device Tree Spec]     [Linux Driver Backports]     [Video for Linux]     [Linux USB Devel]     [Linux Audio Users]     [Linux Kernel]     [Linux SCSI]     [Yosemite Backpacking]

  Powered by Linux