Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit cf5d7e83 authored by Janis Danisevskis's avatar Janis Danisevskis
Browse files

Fix UB in class NullOr

NullOr now stores the references a pointers internally. This fixes UB
where the internal reference was initalized by dereferencing nullptr.

Test: Compiles
Bug: 121390225
Change-Id: I2073e5aeac401309aa63b08e05db3c467fab6b69
parent c8055053
Loading
Loading
Loading
Loading
+35 −24
Original line number Diff line number Diff line
@@ -280,39 +280,50 @@ inline KeyParameter Authorization(TypedTag<tag_type, tag> ttag, Args&&... args)
 */
template <typename ValueT>
class NullOr {
    template <typename T>
    struct reference_initializer {
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wnull-dereference"
        static T&& init() { return *static_cast<std::remove_reference_t<T>*>(nullptr); }
#pragma GCC diagnostic pop
    };
    template <typename T>
    using internal_t = std::conditional_t<std::is_lvalue_reference<ValueT>::value,
                                          std::remove_reference_t<ValueT>*, ValueT>;

    struct pointer_initializer {
        static T init() { return nullptr; }
        static std::nullptr_t init() { return nullptr; }
    };
    template <typename T>
    struct value_initializer {
        static T init() { return T(); }
        static ValueT init() { return ValueT(); }
    };
    template <typename T>
    using initializer_t =
        std::conditional_t<std::is_lvalue_reference<T>::value, reference_initializer<T>,
                           std::conditional_t<std::is_pointer<T>::value, pointer_initializer<T>,
                                              value_initializer<T>>>;
    struct value_pointer_deref_t {
        static ValueT& deref(ValueT& v) { return v; }
    };
    struct reference_deref_t {
        static auto& deref(internal_t v) { return *v; }
    };
    using initializer_t = std::conditional_t<std::is_lvalue_reference<ValueT>::value ||
                                                     std::is_pointer<ValueT>::value,
                                             pointer_initializer, value_initializer>;
    using deref_t = std::conditional_t<std::is_lvalue_reference<ValueT>::value, reference_deref_t,
                                       value_pointer_deref_t>;

  public:
    NullOr() : value_(initializer_t<ValueT>::init()), null_(true) {}
    NullOr(ValueT&& value) : value_(std::forward<ValueT>(value)), null_(false) {}
    NullOr() : value_(initializer_t::init()), null_(true) {}
    template <typename T>
    NullOr(T&& value, typename std::enable_if<
                              !std::is_lvalue_reference<ValueT>::value &&
                                      std::is_same<std::decay_t<ValueT>, std::decay_t<T>>::value,
                              int>::type = 0)
        : value_(std::forward<ValueT>(value)), null_(false) {}
    template <typename T>
    NullOr(T& value, typename std::enable_if<
                             std::is_lvalue_reference<ValueT>::value &&
                                     std::is_same<std::decay_t<ValueT>, std::decay_t<T>>::value,
                             int>::type = 0)
        : value_(&value), null_(false) {}

    bool isOk() const { return !null_; }

    const ValueT& value() const & { return value_; }
    ValueT& value() & { return value_; }
    ValueT&& value() && { return std::move(value_); }
    const ValueT& value() const& { return deref_t::deref(value_); }
    ValueT& value() & { return deref_t::deref(value_); }
    ValueT&& value() && { return std::move(deref_t::deref(value_)); }

  private:
    ValueT value_;
    internal_t value_;
    bool null_;
};