codewitch-honey-crisis / gfx

GFX is a device independent graphics library primarily intended for IoT MCUs but not limited to that.

Home Page:https://honeythecodewitch.com/gfx

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Buffer overflow in gfx_bitmap.hpp

fschuetz opened this issue · comments

In gfx_bitmap.hpp in the class bitmap in function gfx_result point(point16 location,pixel_type* out_pixel) const { on line 182/183 there is a buffer overflow that smashes the stack.

The expression pixel_type::packed_size+(((int)pixel_type::pad_right_bits)<=offs_bits) can evaluate to pixel_type::packed_size allocating a buffer tmp of this size. However, on the following line 183 the buffer tmp is written at tmp[pixel_type::packed_size]=0; This is out of bounds by 1.

I did not yet fully dig in the logic, but either the buffer allocation should be increased by 1 on line 182
uint8_t tmp[pixel_type::packed_size+(((int)pixel_type::pad_right_bits)<=offs_bits)+1];
or the terminating 0 should be written to pixel_type::packed_size-1 on line 183
tmp[pixel_type::packed_size-1]=0;

Hi. I'm really reluctant to modify this portion of the code without code that can reproduce the problem. This code has been in use as is in production nearly since the file was written. I hope you understand my concern. Can you produce a main.cpp that can be dropped in a PIO project to reproduce the scenario?

Sure, I could do that when I get around to it, but its maybe faster and easier for you to reproduce as one of your examples how to load a jpeg image triggers the fault - and you might already have a fully configured project for this.

To reproduce on a ESP32 set "Stack smashing protection mode" to "Overall" in menuconfig (CONFIG_COMPILER_STACK_CHECK_MODE_ALL=y and CONFIG_COMPILER_STACK_CHECK=y).

// TODO FAILURE - This smashes the stack.
jpeg_image::load(&fs,[](size16 dimensions,
                                typename jpeg_image::region_type& region,
                                point16 location,
                                void* state) {
            // use draw:: to render this portion to the display
            return draw::bitmap(lcd, // lcd is available globally
                                srect16((spoint16)location,
                                        (ssize16)region.dimensions()),
                                        region,region.bounds());
        // we don't need state, so just use nullptr
        },nullptr);

As to your concern on why its been used in production but did not trigger a failure: This is due to the nature of weak stack protection. This off by one error likely does not influence program flow, as the array is overshot by only one byte with the line tmp[pixel_type::packed_size]=0. The rest of the function does in almost any cases not rely on that memory area and due to the likely position of the array on the stack it does not write past the stack frame (and thus is not detected by stack canaries which are used by "normal" stack protection. If however you use strong stack protection that adds guards to all arrays, then it will trigger.

For me making the buffer +1 bigger is the correct solution and it works in all tests I did, but as I said I did not fully verify that the logic would not rather be to keep the size and change the 0 termination.

Thank you so much for the information. I will review the code and implement the necessary fix. I just want to make 100% certain +1 is the appropriate answer. There's an alternative that may be correct, but I'll look into it. Thanks again.

I will dig into that if you do not have the time. I just didn't get around to it yet. And let me know if you cannot reproduce so I can make this minimal example.

I actually already did so and put in the fix you suggested. The latest release is 1.3.9 and should be in the PIO repo by now. I really appreciate all the help. You've been great.

Thanks a lot. That was quick.