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

Bug in LCDPattern #63

Open
jhbruhn opened this issue Sep 10, 2023 · 8 comments
Open

Bug in LCDPattern #63

jhbruhn opened this issue Sep 10, 2023 · 8 comments
Labels
bug Something isn't working

Comments

@jhbruhn
Copy link

jhbruhn commented Sep 10, 2023

There seems to be a bug in the LCDPattern implementation. When using the draw_line method (and possibly others), the Pattern is drawn with random data (changes for example on button inputs) instead of the selected pattern (in my case a checkerboard for grayscale).

This was only tested on the simulator though, as I don't have real hardware yet!

Here is an example. The leftmost line should have every second pixel on, but it's actually even more (and changes in more complex programs). The second line is supposed to be solid.

#![no_std]

extern crate alloc;

use {
    alloc::boxed::Box,
    anyhow::Error,
    crankstart::{
        crankstart_game,
        graphics::{Graphics, LCDColor, LCDSolidColor},
        system::System,
        Game, Playdate,
    },
    crankstart_sys::{LCDPattern},
    euclid::{point2},
};

const GRAY_PATTERN: LCDPattern = [0x55, 0xAA, 0x55, 0xAA, 0x55, 0xAA, 0x55, 0xAA, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff];

struct State {
}

impl State {
    pub fn new(_playdate: &Playdate) -> Result<Box<Self>, Error> {
        crankstart::display::Display::get().set_refresh_rate(20.0)?;
        Ok(Box::new(Self {
        }))
    }
}

impl Game for State {
    fn update(&mut self, _playdate: &mut Playdate) -> Result<(), Error> {
        let graphics = Graphics::get();
        graphics.clear(LCDColor::Solid(LCDSolidColor::kColorWhite))?;
        graphics.draw_line(point2(20, 0), point2(20, 200), 1, LCDColor::Pattern(GRAY_PATTERN));
        graphics.draw_line(point2(40, 200), point2(40, 0), 1, LCDColor::Solid(LCDSolidColor::kColorBlack));

        System::get().draw_fps(0, 0)?;

        Ok(())
    }
}

crankstart_game!(State);
@boozook
Copy link
Member

boozook commented Sep 10, 2023

I suppose that pattern was dropped. Inner implementation of color is

  • usize 0..=3 is color
  • usize >4 is interpreted as pointer to array 8*8

So there's pointer to const array, temp value on stack.
I'll test, but could you try make it static or store anywhere?

There's almost same implementation but with lifetime and ownership.

@parasyte
Copy link
Contributor

parasyte commented Oct 2, 2023

Yikes! 😳

Yes, this is a use-after-free. LCDColor::Pattern(GRAY_PATTERN) is constructed on the stack and dropped after from returns:

impl From<LCDColor> for usize {
fn from(color: LCDColor) -> Self {
match color {
LCDColor::Solid(solid_color) => solid_color as usize,
LCDColor::Pattern(pattern) => {
let pattern_ptr = &pattern as *const u8;
pattern_ptr as usize
}
}
}
}

The implementation in @boozook's crate (owned variant) has the same problem for the same reason. The return value from from() points at dropped memory.


From what I can tell, the PatternRef variant in the linked crate is sound.

@boozook
Copy link
Member

boozook commented Oct 2, 2023

Agreed. This is next in my queue.

@boozook
Copy link
Member

boozook commented Oct 3, 2023

Any suggestions?
I have no good idea yet, just allow to store only &'static pattern in the LCDColor::Pattern (e.g.).
Also maybe allow something with !Unpin 🤷🏻‍♂️

I'll look at it tomorrow. Here is deep night now.

@boozook boozook pinned this issue Oct 3, 2023
@boozook
Copy link
Member

boozook commented Oct 3, 2023

Also there is problem with bitmap.set_color_to_pattern.

  1. create color
  2. create filled bitmap
  3. bitmap.set_color_to_pattern(&mut color) (or something like that)
  4. change or drop bitmap
    Color, as we know, is ptr points to array allocated somewhere.
    And it isn't changed at step 4! Seems to it's our own array from now.
    So when it should be feed? What padding, what preserved capacity?

Update:
No, if try to free it => SIGSEGV in sim. Not tested on the device.

@parasyte
Copy link
Contributor

parasyte commented Oct 3, 2023

I believe any lifetime will work. It just might be a bit of an ergonomics issue for users. I tested this yesterday with MIRI and it seemed ok (apart from the int-pointer casts, which raised some warnings).

https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=dac65cfbabee99da198f4f99c2d838a3

(edit: this is not in reference to bitmap.set_color_to_pattern)

@boozook boozook added the bug Something isn't working label Oct 3, 2023
@parasyte
Copy link
Contributor

parasyte commented Oct 3, 2023

Do I take it that the assignment means you want (one of us) to submit a PR for this issue?

@boozook
Copy link
Member

boozook commented Oct 4, 2023

It with be great.
Of course, no pressure, just say no if you don't want to. The assignment is just a hint to attract attention.

paulstraw added a commit to strawdynamics/crankstart that referenced this issue Mar 28, 2024
The existing Pattern(LCDPattern) has memory issues. See pd-rs#63 for more details.

Had to remove the TextSprite helper, but c'est la vie.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

No branches or pull requests

3 participants