Repository navigation
[0.4.x] libvisual: Fix palette use-after-free (when switching actors) - #214
Conversation
| visual_log (VISUAL_LOG_DEBUG, "Entering..."); | ||
| if (bin->managed == TRUE) | ||
| visual_object_unref (VISUAL_OBJECT (bin->actor)); | ||
| visual_video_set_palette (bin->actvideo, NULL); |
There was a problem hiding this comment.
@hartwork, where does the use-after-free happen? This also seems like an odd place to reset the palette. I feel that there should be a better place to stick this line in.
There was a problem hiding this comment.
@kaixiong what I observed was that the palette of one plug-in would be used by the next one, because nothing was resetting the video palette on actor cleanup (where the free is happening) and then the first time that update_all runs in lv-tool, the new plugin has not been asked for its palette yet. While writing that, maybe we need changes in two places: avoiding the stale palette pointer but also getting a fresh one in before the first render. I'm happy to put these wherever fits best. What do you think?
There was a problem hiding this comment.
@hartwork That sounds like a good approach. I'm wondering how much work that is though.
If that's a lot of effort, I'm okay with fixing it like this (it's a critical bug after all) but with a big FIXME comment added above it so it can be re-looked at later.
There was a problem hiding this comment.
@kaixiong I went a bit further with this, let me know how you like the new version.
af437e5 to
55b7a2e
Compare
9f7dc89 to
73ac23b
Compare
| : std::min(target_colors, static_cast<visual_size_t>(pal->ncolors)); | ||
| std::array<SDL_Color, target_colors> colors {}; | ||
| for (visual_size_t i = 0; i < source_colors; i++) { | ||
| colors[i].r = pal->colors[i].r; |
There was a problem hiding this comment.
@hartwork hmm.. I'm not sure I understand why this fixes the reported use-after-free.
On the change itself:
The entire 256-entry palette should be set, so if the source palette is smaller than that, the remaining entries should be assigned black. This is the original behaviour and #215 was where it was pointed out. Unfortunately, #213 was already applied and now 0.4 has the altered behaviour.
The other thing is that I'd prefer to use std::size_t instead of visual_size_t.
Finally, I think target_ncolors or target_color_count is better than 'target_colors`, a name that tends to suggest colour values, rather than a count of them.
There was a problem hiding this comment.
@hartwork hmm.. I'm not sure I understand why this fixes the reported use-after-free.
@kaixiong thanks for the review! Strictly speaking, there are three (related) things going on in here:
- Set the pointer to
NULLthat lived longer than it should have. That technically fixes the use-after-free already. - The second things is that not only do we need to get rid of the old palette, we also need to get in the new applied. There was a code path where that didn't happen, previously. That fix is the second part.
- The third part is improving the upload of the palette to SDL, more on that below.
On the change itself:
The entire 256-entry palette should be set
Ack!
, so if the source palette is smaller than that, the remaining entries should be assigned black.
Ack!
This is the original behaviour
Not on 0.4.x, it copies full 256 entries or nothing at the moment:
libvisual/libvisual/tools/lv-tool/lv-tool.cpp
Lines 410 to 414 in 855f256
and #215 was where it was pointed out.
Yes, but that was master and we didn't apply it because it was mistaken and you caught it during review 👍
Unfortunately, #213 was already applied and now 0.4 has the altered behaviour.
Pull request #213 fixed out-of-bounds access but did not change behavior beyond that. Now pull request #214 right here tries to get it right.
The other thing is that I'd prefer to use
std::size_tinstead ofvisual_size_t.
Can do, give me a minute.
Finally, I think
target_ncolorsortarget_color_countis better than 'target_colors`, a name that tends to suggest colour values, rather than a count of them.
Ack, will adjust in a minute.
There was a problem hiding this comment.
The other thing is that I'd prefer to use
std::size_tinstead ofvisual_size_t.Can do, give me a minute.
Finally, I think
target_ncolorsortarget_color_countis better than 'target_colors`, a name that tends to suggest colour values, rather than a count of them.Ack, will adjust in a minute.
@kaixiong done
There was a problem hiding this comment.
@hartwork Thanks. I have another suggestion. Instead of having that complicated looking std::min() expression with a ternary operator, why not use an if block?
std::array<SDL_Color, 256> colors {};
if (pal) {
const std::size_t pal_color_count = std::min(colors.size(), static_cast<visual_size_t>(pal->ncolors));
for (std::size_t i = 0; i < pal_color_count; i++) {
colors[i].r = pal->colors[i].r;
colors[i].g = pal->colors[i].g;
colors[i].b = pal->colors[i].b;
}
}
SDL_SetColors (m_screen, colors.data(), 0, colors.size());fed5592 to
4dfd5af
Compare
316408f to
5ccb250
Compare
17bf096 to
04bbd77
Compare
04bbd77 to
f1a1588
Compare
f1a1588 to
981b9a2
Compare
|
@hartwork, sorry for the delay. Let me review the use-after-free fix soon. |
|
@kaixiong do we need any more improvements here before a merge? |
Can be reproduced by running
lv-tool --exclude projectM,gforce --switch 10with AddressSanitizer active.