Re: [PATCH v4 2/6] Add a way to control the level of checks in the code

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



Hi David,

On Tue, 28 Jan 2020 at 01:37, David Gibson <david@xxxxxxxxxxxxxxxxxxxxx> wrote:
>
> On Tue, Nov 12, 2019 at 06:54:18PM -0700, Simon Glass wrote:
> > Add a new ASSUME_MASK option, which allows for some control over the
> > checks used in libfdt. With all assumptions enabled, libfdt assumes that
> > the input data and parameters are all correct and that internal errors
> > cannot happen.
> >
> > By default no assumptions are made and all checks are enabled.
> >
> > Signed-off-by: Simon Glass <sjg@xxxxxxxxxxxx>
> > ---
> >
> > Changes in v4:
> > - Add a can_assume() macros and squash the inline functions into one
> > - Drop unnecessary FDT_ prefix
> > - Fix 'Incosistencies' typo
> > - Merge the 'friendly' and 'sane' checks into one
> > - Update and expand comments
> >
> > Changes in v3:
> > - Expand the comments about each check option
> > - Invert the checks to be called assumptions
> > - Move header-version code to fdt.c
> > - Move the comments on check options to the enum
> > - Use hex for CHK_MASK
> >
> > Changes in v2:
> > - Add an fdt_ prefix to avoid namespace conflicts
> > - Use symbolic names for _check functions and drop leading underscores
> >
> >  Makefile                 |  6 ++-
> >  libfdt/libfdt_internal.h | 88 ++++++++++++++++++++++++++++++++++++++++
> >  2 files changed, 93 insertions(+), 1 deletion(-)
> >
> > diff --git a/Makefile b/Makefile
> > index 4d88157..bdeaf93 100644
> > --- a/Makefile
> > +++ b/Makefile
> > @@ -16,7 +16,11 @@ EXTRAVERSION =
> >  LOCAL_VERSION =
> >  CONFIG_LOCALVERSION =
> >
> > -CPPFLAGS = -I libfdt -I .
> > +# Control the assumptions made (e.g. risking security issues) in the code.
> > +# See libfdt_internal.h for details
> > +ASSUME_MASK ?= 0
> > +
> > +CPPFLAGS = -I libfdt -I . -DFDT_ASSUME_MASK=$(ASSUME_MASK)
> >  WARNINGS = -Wall -Wpointer-arith -Wcast-qual -Wnested-externs \
> >       -Wstrict-prototypes -Wmissing-prototypes -Wredundant-decls -Wshadow
> >  CFLAGS = -g -Os $(SHAREDLIB_CFLAGS) -Werror $(WARNINGS) $(EXTRA_CFLAGS)
> > diff --git a/libfdt/libfdt_internal.h b/libfdt/libfdt_internal.h
> > index 058c735..198480c 100644
> > --- a/libfdt/libfdt_internal.h
> > +++ b/libfdt/libfdt_internal.h
> > @@ -48,4 +48,92 @@ static inline struct fdt_reserve_entry *fdt_mem_rsv_w_(void *fdt, int n)
> >
> >  #define FDT_SW_MAGIC         (~FDT_MAGIC)
> >
> > +/**********************************************************************/
> > +/* Checking controls                                                  */
> > +/**********************************************************************/
> > +
> > +#ifndef FDT_ASSUME_MASK
> > +#define FDT_ASSUME_MASK 0
> > +#endif
> > +
> > +/*
> > + * Defines assumptions which can be enabled. Each of these can be enabled
> > + * individually. For maximum saftey, don't enable any assumptions!
> > + *
> > + * For minimal code size and no safety, use ASSUME_PERFECT at your own risk.
> > + * You should have another method of validating the device tree, such as a
> > + * signature or hash check before using libfdt.
> > + *
> > + * For situations where security is not a concern it may be safe to enable
> > + * ASSUME_SANE.
> > + */
> > +enum {
> > +     /*
> > +      * This does essentially no checks. Only the latest device-tree
> > +      * version is correctly handled. Inconsistencies or errors in the device
> > +      * tree may cause undefined behaviour or crashes.
>
> Likewise for errors in parameters, yes?

Will add

>
> > +      * If an error occurs when modifying the tree it may leave the tree in
> > +      * an intermediate (but valid) state. As an example, adding a property
> > +      * where there is insufficient space may result in the property name
> > +      * being added to the string table even though the property itself is
> > +      * not added to the struct section.
> > +      *
> > +      * Only use this if you have a fully validated device tree with
> > +      * the latest supported version and wish to minimise code size.
> > +      */
> > +     ASSUME_PERFECT          = 0xff,
> > +
> > +     /*
> > +      * This assumes that the device tree is sane. i.e. header metadata
> > +      * and basic hierarchy are correct.
> > +      *
> > +      * It also assumes libfdt functions are called with valid parameters,
> > +      * i.e. not trigger FDT_ERR_BADOFFSET or offsets that are out of bounds.
> > +      * It disables any extensive checking of parameters and the device
> > +      * tree, making various assumptions about correctness. Normal device
> > +      * trees produced by libfdt and the compiler should be handled safely.
> > +      * Malicious device trees and complete garbage may cause libfdt to
> > +      * behave badly or crash.
> > +      *
> > +      * These checks will be sufficient if you have a valid device tree with
> > +      * no internal inconsistencies and call libfdt with correct parameters.
> > +      * With this assumption, libfdt will generally not return
> > +      * -FDT_ERR_INTERNAL, -FDT_ERR_BADLAYOUT, etc.
> > +      */
> > +     ASSUME_SANE             = 1 << 0,
>
> How tricky would it be to split this into ASSUME_VALID_DTB and
> ASSUME_VALID_PARAMETERS?  I don't know useful those are on their own,
> but it seems to me it might be easier to define the effect of this if
> split that way.

>From my reading of the checks I've added, we could split out the
ASSUME_VALID_DTB assumption and that would have some checks in it
(e.g. in fdt_check_header()).

But for parameters the code doesn't currently check whether the DT or
the argument is wrong. E.g.:

const void *fdt_offset_ptr(const void *fdt, int offset, unsigned int len)
{
unsigned absoffset = offset + fdt_off_dt_struct(fdt);

if (!can_assume(SANE))
if ((absoffset < offset)
    || ((absoffset + len) < absoffset)
    || (absoffset + len) > fdt_totalsize(fdt))
return NULL;

Either the struct offset or @offset could be wrong here.

I think we could use ASSUME_VALID_DTB for the checks where it makes
sense. For the others we could perhaps assume that the DTB is valid
and blame any failure on parameters. So:

- anything that has no parameters in the check would use ASSUME_VALID_DTB
- anything else would use ASSUME_VALID_PARAMETERS

What do you think?

>
> > +
> > +     /*
> > +      * This disables checks for device-tree version and removes all code
> > +      * which handles older versions.
> > +      *
> > +      * Only enable this if you know you have a device tree with the latest
> > +      * version.
> > +      */
> > +     ASSUME_LATEST           = 1 << 1,
> > +
> > +     /*
> > +      * This assume that it is OK for a failed additional to the device tree
> > +      * due to lack of space or some other problem can skip any rollback
> > +      * steps (such as dropping the property name from the string table).
> > +      * This is safe to enable in most circumstances, even though it may
> > +      * leave the tree in a sub-optimal state.
> > +      */
> > +     ASSUME_NO_ROLLBACK      = 1 << 2,
> > +};
> > +
> > +/**
> > + * _can_assume() - check if a particular assumption is enabled
>
> Don't use a leading _ please, that's reserved for system libraries.
> I've moved to trailing _ for that reason.

OK.

>
> > + *
> > + * @mask: Mask to check (ASSUME_...)
> > + * @return true if that assumption is enabled, else false
> > + */
> > +static inline bool _can_assume(int mask)
> > +{
> > +     return FDT_ASSUME_MASK & mask;
> > +}
> > +
> > +/** helper macros for checking assumptions */
> > +#define can_assume(_assume)  _can_assume(ASSUME_ ## _assume)
> > +
> >  #endif /* LIBFDT_INTERNAL_H */

Regards,
Simon



[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