Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for different inputs to wil::reg::set_value_binary #399

Open
ChrisGuzak opened this issue Nov 28, 2023 · 4 comments
Open

Add support for different inputs to wil::reg::set_value_binary #399

ChrisGuzak opened this issue Nov 28, 2023 · 4 comments
Labels
feature-request New feature or request

Comments

@ChrisGuzak
Copy link
Member

ChrisGuzak commented Nov 28, 2023

I'd like to be able to pass data in formats other than std::vector<uint8_t> as input to binary registry data.

While I can write this

wil::reg::set_value_binary(HKEY_CURRENT_USER, L"Value", REG_BINARY, std::vector<uint8_t>{1, 2, 3});

This is not supported

    struct MyData
    {
        int a;
        int b;
    };
    wil::reg::set_value_binary(HKEY_CURRENT_USER, L"Value", REG_BINARY, std::vector<MyData>{{1, 2}, {3, 4}});

I'd also like to be able to do this.

    struct POD { int a; int b; };
    POD pod{};
    wil::reg::set_value_binary(HKEY_CURRENT_USER, L"Value", REG_BINARY, pod);

or some support void*/size_t as the input.

@dunhor dunhor added the feature-request New feature or request label Nov 29, 2023
@dunhor
Copy link
Member

dunhor commented Dec 4, 2023

Just realized that you can't call the throwing version without passing a std::vector and you can't call the non-throwing version without passing a unique_cotaskmem_array_ptr - OUCH! Would love to see this - and possibly others; haven't checked - changed so that the input type isn't so constrained. ESPECIALLY to a dynamically allocated type.

@jonwis
Copy link
Member

jonwis commented May 27, 2024

OK, so I'll add support for this:

Spanlikes of PODs - generally, anything that can has a T* data() and size() where T is POD. This covers vector and array and span. It also happens to cover std::array<uint8_t> since that clear has a .data()

PODs - can have its address taken and has a fixed size. Convertable to a span-like with addressof/sizeof.

Can probably get this to also remove the direct dependency on vector's existence.

@jonwis
Copy link
Member

jonwis commented May 28, 2024

OK, got this far so far as a prototype. My template-fu is breaking down, I can't get the detectoid to block vector<T*> yet.

#include <vector>
#include <string>
#include <array>

struct MyData {
    int a;
    int b;
};

void set_value(void const *ptr, size_t size);

template<typename Q> using always_false = std::false_type;

template<typename Q> void set(Q const& q)
{
    constexpr bool is_span_like = requires {
        { *q.data() };
        { q.size() };
    };

    if constexpr (is_span_like)
    {
        static_assert(std::is_pod_v<std::decay_t<decltype(*q.data())>>);
        static_assert(!std::is_pointer_v<decltype(*q.data())>);
        set_value(q.data(), q.size() * sizeof(*q.data()));
    }
    else if constexpr (std::is_array_v<Q>)
    {
        static_assert(!std::is_pointer_v<std::remove_all_extents_t<Q>>);
        set_value(q, sizeof(q));
    }
    else if constexpr (std::is_pod_v<Q>)
    {
        set_value(std::addressof(q), sizeof(q));
    }
    else
    {
        static_assert(always_false<Q>());
    }
}

void should_work()
{
    set(std::vector<int>{1, 2, 3});
    set(std::vector<float>{0.5f, 2.1f, 0.3f, 10.0f});
    set(std::vector<MyData>{{6, 7}});
    set(std::array<int, 3>{1,2,3});

    set(int(3));
    set(float{3});
    set(MyData{1, 2});

    MyData r[] = {{3, 4}, {5, 6}};
    set(r);
}

void does_not_work()
{
    // Reasoning: wstring is "complex"
    // set(std::vector<std::wstring>{L"kittens", L"puppies"});

    // Reasoning: int* is not POD and not safely blittable into binary
    // int* p[2]{};
    // set(p);
    set(std::array<int*, 3>{nullptr, nullptr, nullptr});
    set(std::vector<MyData*>{});
}

void should_probably_not_work_2()
{
    set(std::wstring(L"puppies"));
}

@citelao
Copy link
Contributor

citelao commented Aug 26, 2024

Are POD types portably (de)serializable? I'd be very worried about supporting any type other than a pure list of bytes; you'd probably want to use capnproto or protobufs as an intermediate, right?

It seems very easy to write something that seems secure but is not. e.g. what about endianness? buffer overruns? eek

void* or std::array_view<uint8_t> might be good overloads, but supporting somewhat-arbitrary types (even plain-old-data types) scares me


Plus: because there's no ordering or structuring to the data type, I can easily imagine folks changing member ordering and blowing up their whole "deserialization" pipeline. This seems like a big footgun.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
feature-request New feature or request
Projects
None yet
Development

No branches or pull requests

4 participants