mirror of
https://github.com/Rockbox/rockbox.git
synced 2025-11-09 13:12:37 -05:00
PictureFlow: Fix buffer overflow
create_track_index appears to have relied on buflib_buffer_out returning a certain amount of space without checking that it was actually available. In at least one test case, as little as 16 bytes were returned, leading to a buffer overflow and later a segfault. Change-Id: Ic0783f3cd5bf015803b7ce90537ba38ab3434bea
This commit is contained in:
parent
e3b8b7fa80
commit
dded97be34
1 changed files with 126 additions and 101 deletions
|
|
@ -330,6 +330,7 @@ struct pf_track_t {
|
||||||
int list_y;
|
int list_y;
|
||||||
int list_h;
|
int list_h;
|
||||||
size_t borrowed;
|
size_t borrowed;
|
||||||
|
size_t used;
|
||||||
struct track_data *index;
|
struct track_data *index;
|
||||||
char *names;
|
char *names;
|
||||||
};
|
};
|
||||||
|
|
@ -1665,13 +1666,109 @@ static int compare_tracks (const void *a_v, const void *b_v)
|
||||||
return (int)(a - b);
|
return (int)(a - b);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
static bool track_buffer_avail(size_t needed)
|
||||||
|
{
|
||||||
|
size_t total_out = 0;
|
||||||
|
size_t out = 0;
|
||||||
|
if (pf_tracks.borrowed == 0 && pf_tracks.used == 0)
|
||||||
|
{
|
||||||
|
pf_tracks.names = rb->buflib_buffer_out(&buf_ctx, &out);
|
||||||
|
pf_tracks.borrowed = out;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (needed <= pf_tracks.borrowed - pf_tracks.used)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
while (needed > (pf_tracks.borrowed + total_out) - pf_tracks.used)
|
||||||
|
{
|
||||||
|
if (!free_slide_prio(0))
|
||||||
|
break;
|
||||||
|
out = 0;
|
||||||
|
rb->buflib_buffer_out(&buf_ctx, &out);
|
||||||
|
total_out += out;
|
||||||
|
}
|
||||||
|
pf_tracks.borrowed += total_out;
|
||||||
|
|
||||||
|
// have to move already stored track_data structs
|
||||||
|
if (pf_tracks.count)
|
||||||
|
{
|
||||||
|
struct track_data *new_tracks = (struct track_data *)(total_out + (uintptr_t)pf_tracks.index);
|
||||||
|
unsigned int bytes = pf_tracks.count * sizeof(struct track_data);
|
||||||
|
rb->memmove(new_tracks, pf_tracks.index, bytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (needed > pf_tracks.borrowed - pf_tracks.used)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int pf_tcs_retrieve_track_title(int string_index, int disc_num, int track_num)
|
||||||
|
{
|
||||||
|
char file_name[MAX_PATH];
|
||||||
|
char *track_title = NULL;
|
||||||
|
int str_len;
|
||||||
|
|
||||||
|
if (rb->strcmp(UNTAGGED, tcs.result) == 0)
|
||||||
|
{
|
||||||
|
/* show filename instead of <untaggged> */
|
||||||
|
if (!rb->tagcache_retrieve(&tcs, tcs.idx_id, tag_filename,
|
||||||
|
file_name, MAX_PATH))
|
||||||
|
return 0;
|
||||||
|
track_title = file_name;
|
||||||
|
if (track_title)
|
||||||
|
{
|
||||||
|
/* if filename remove the '/' */
|
||||||
|
track_title = rb->strrchr(track_title, PATH_SEPCH);
|
||||||
|
if (track_title)
|
||||||
|
track_title++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!track_title)
|
||||||
|
track_title = tcs.result;
|
||||||
|
|
||||||
|
int max_len = rb->strlen(track_title) + 10;
|
||||||
|
if (!track_buffer_avail(max_len))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if (track_num > 0)
|
||||||
|
{
|
||||||
|
if (disc_num > 0)
|
||||||
|
str_len = rb->snprintf(pf_tracks.names + string_index, max_len,
|
||||||
|
"%d.%02d: %s", disc_num, track_num, track_title);
|
||||||
|
else
|
||||||
|
str_len = rb->snprintf(pf_tracks.names + string_index, max_len,
|
||||||
|
"%d: %s", track_num, track_title);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
str_len = rb->snprintf(pf_tracks.names + string_index, max_len,
|
||||||
|
"%s", track_title);
|
||||||
|
return str_len;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if PF_PLAYBACK_CAPABLE
|
||||||
|
static int pf_tcs_retrieve_file_name(int fn_idx)
|
||||||
|
{
|
||||||
|
if (!track_buffer_avail(MAX_PATH))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
rb->tagcache_retrieve(&tcs, tcs.idx_id, tag_filename,
|
||||||
|
pf_tracks.names + fn_idx, MAX_PATH);
|
||||||
|
|
||||||
|
return rb->strlen(pf_tracks.names + fn_idx);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Create the track index of the given slide_index.
|
Create the track index of the given slide_index.
|
||||||
*/
|
*/
|
||||||
static void create_track_index(const int slide_index)
|
static void create_track_index(const int slide_index)
|
||||||
{
|
{
|
||||||
buf_ctx_lock();
|
buf_ctx_lock();
|
||||||
char temp[MAX_PATH + 1];
|
|
||||||
if ( slide_index == pf_tracks.cur_idx )
|
if ( slide_index == pf_tracks.cur_idx )
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
|
@ -1682,124 +1779,48 @@ static void create_track_index(const int slide_index)
|
||||||
pf_idx.album_index[slide_index].seek);
|
pf_idx.album_index[slide_index].seek);
|
||||||
|
|
||||||
if (pf_idx.album_index[slide_index].artist_idx >= 0)
|
if (pf_idx.album_index[slide_index].artist_idx >= 0)
|
||||||
{
|
|
||||||
rb->tagcache_search_add_filter(&tcs, tag_albumartist,
|
rb->tagcache_search_add_filter(&tcs, tag_albumartist,
|
||||||
pf_idx.album_index[slide_index].artist_seek);
|
pf_idx.album_index[slide_index].artist_seek);
|
||||||
}
|
|
||||||
|
|
||||||
int string_index = 0, track_num;
|
|
||||||
int disc_num;
|
|
||||||
|
|
||||||
char* result = NULL;
|
|
||||||
size_t out = 0;
|
|
||||||
|
|
||||||
|
int string_index = 0;
|
||||||
pf_tracks.count = 0;
|
pf_tracks.count = 0;
|
||||||
pf_tracks.names = rb->buflib_buffer_out(&buf_ctx, &out);
|
|
||||||
pf_tracks.borrowed += out;
|
|
||||||
int avail = pf_tracks.borrowed;
|
|
||||||
pf_tracks.index = (struct track_data*)(pf_tracks.names + pf_tracks.borrowed);
|
|
||||||
while (rb->tagcache_get_next(&tcs))
|
while (rb->tagcache_get_next(&tcs))
|
||||||
{
|
{
|
||||||
result = NULL;
|
int disc_num = rb->tagcache_get_numeric(&tcs, tag_discnumber);
|
||||||
if (rb->strcmp(UNTAGGED, tcs.result) == 0)
|
int track_num = rb->tagcache_get_numeric(&tcs, tag_tracknumber);
|
||||||
{
|
disc_num = disc_num > 0 ? disc_num : 0;
|
||||||
/* show filename instead of <untaggged> */
|
track_num = track_num > 0 ? track_num : 0;
|
||||||
if (!rb->tagcache_retrieve(&tcs, tcs.idx_id, tag_filename,
|
int fn_idx = 1 + pf_tcs_retrieve_track_title(string_index, disc_num, track_num);
|
||||||
temp, sizeof(temp) - 1))
|
if (fn_idx <= 1)
|
||||||
{
|
|
||||||
goto fail;
|
|
||||||
}
|
|
||||||
result = temp;
|
|
||||||
}
|
|
||||||
|
|
||||||
int len = 0, fn_idx = 0;
|
|
||||||
|
|
||||||
avail -= sizeof(struct track_data);
|
|
||||||
track_num = rb->tagcache_get_numeric(&tcs, tag_tracknumber);
|
|
||||||
disc_num = rb->tagcache_get_numeric(&tcs, tag_discnumber);
|
|
||||||
|
|
||||||
if (result)
|
|
||||||
{
|
|
||||||
/* if filename remove the '/' */
|
|
||||||
result = rb->strrchr(result, PATH_SEPCH);
|
|
||||||
if (result)
|
|
||||||
result++;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!result)
|
|
||||||
result = tcs.result;
|
|
||||||
|
|
||||||
if (disc_num < 0)
|
|
||||||
disc_num = 0;
|
|
||||||
retry:
|
|
||||||
if (track_num > 0)
|
|
||||||
{
|
|
||||||
if (disc_num)
|
|
||||||
fn_idx = 1 + rb->snprintf(pf_tracks.names + string_index, avail,
|
|
||||||
"%d.%02d: %s", disc_num, track_num, result);
|
|
||||||
else
|
|
||||||
fn_idx = 1 + rb->snprintf(pf_tracks.names + string_index, avail,
|
|
||||||
"%d: %s", track_num, result);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
track_num = 0;
|
|
||||||
fn_idx = 1 + rb->snprintf(pf_tracks.names + string_index, avail,
|
|
||||||
"%s", result);
|
|
||||||
}
|
|
||||||
if (fn_idx <= 0)
|
|
||||||
goto fail;
|
goto fail;
|
||||||
|
pf_tracks.used += fn_idx;
|
||||||
|
|
||||||
#if PF_PLAYBACK_CAPABLE
|
#if PF_PLAYBACK_CAPABLE
|
||||||
int remain = avail - fn_idx;
|
int fn_len = 1 + pf_tcs_retrieve_file_name(string_index + fn_idx);
|
||||||
if (remain >= MAX_PATH)
|
if (fn_len <= 1)
|
||||||
{ /* retrieve filename for building the playlist */
|
goto fail;
|
||||||
rb->tagcache_retrieve(&tcs, tcs.idx_id, tag_filename,
|
pf_tracks.used += fn_len;
|
||||||
pf_tracks.names + string_index + fn_idx, remain);
|
|
||||||
|
|
||||||
len = fn_idx + rb->strlen(pf_tracks.names + string_index + fn_idx) + 1;
|
|
||||||
/* make sure track name and file name are really split by a \0, else
|
|
||||||
* get_track_name might fail */
|
|
||||||
*(pf_tracks.names + string_index + fn_idx -1) = '\0';
|
|
||||||
|
|
||||||
}
|
|
||||||
else /* request more buffer so that track and filename fit */
|
|
||||||
len = (avail - remain) + MAX_PATH;
|
|
||||||
#else
|
|
||||||
len = fn_idx;
|
|
||||||
#endif
|
#endif
|
||||||
if (len > avail)
|
if (!track_buffer_avail(sizeof(struct track_data)))
|
||||||
{
|
goto fail;
|
||||||
while (len > avail)
|
|
||||||
{
|
|
||||||
if (!free_slide_prio(0))
|
|
||||||
goto fail;
|
|
||||||
out = 0;
|
|
||||||
rb->buflib_buffer_out(&buf_ctx, &out);
|
|
||||||
avail += out;
|
|
||||||
pf_tracks.borrowed += out;
|
|
||||||
|
|
||||||
struct track_data *new_tracks;
|
pf_tracks.used += sizeof(struct track_data);
|
||||||
new_tracks = (struct track_data *)(out + (uintptr_t)pf_tracks.index);
|
unsigned int arr_sz = (pf_tracks.count + 1) * sizeof(struct track_data);
|
||||||
|
// Arrray descends from upper end of buflib-borrowed buffer.
|
||||||
unsigned int bytes = pf_tracks.count * sizeof(struct track_data);
|
pf_tracks.index = (struct track_data*)(pf_tracks.names + pf_tracks.borrowed
|
||||||
if (pf_tracks.count)
|
- arr_sz );
|
||||||
rb->memmove(new_tracks, pf_tracks.index, bytes);
|
|
||||||
pf_tracks.index = new_tracks;
|
|
||||||
}
|
|
||||||
goto retry;
|
|
||||||
}
|
|
||||||
|
|
||||||
avail -= len;
|
|
||||||
pf_tracks.index--;
|
|
||||||
pf_tracks.index->sort = (disc_num << 24) + (track_num << 14);
|
pf_tracks.index->sort = (disc_num << 24) + (track_num << 14);
|
||||||
pf_tracks.index->sort += pf_tracks.count;
|
pf_tracks.index->sort += pf_tracks.count;
|
||||||
pf_tracks.index->name_idx = string_index;
|
pf_tracks.index->name_idx = string_index;
|
||||||
pf_tracks.index->seek = tcs.result_seek;
|
pf_tracks.index->seek = tcs.result_seek;
|
||||||
#if PF_PLAYBACK_CAPABLE
|
#if PF_PLAYBACK_CAPABLE
|
||||||
pf_tracks.index->filename_idx = fn_idx + string_index;
|
pf_tracks.index->filename_idx = fn_idx + string_index;
|
||||||
|
string_index += (fn_idx + fn_len);
|
||||||
|
#else
|
||||||
|
string_index += fn_idx;
|
||||||
#endif
|
#endif
|
||||||
pf_tracks.count++;
|
pf_tracks.count++;
|
||||||
string_index += len;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
rb->tagcache_search_finish(&tcs);
|
rb->tagcache_search_finish(&tcs);
|
||||||
|
|
@ -1824,6 +1845,7 @@ static inline void free_borrowed_tracks(void)
|
||||||
{
|
{
|
||||||
rb->buflib_buffer_in(&buf_ctx, pf_tracks.borrowed);
|
rb->buflib_buffer_in(&buf_ctx, pf_tracks.borrowed);
|
||||||
pf_tracks.borrowed = 0;
|
pf_tracks.borrowed = 0;
|
||||||
|
pf_tracks.used = 0;
|
||||||
pf_tracks.cur_idx = -1;
|
pf_tracks.cur_idx = -1;
|
||||||
buf_ctx_unlock();
|
buf_ctx_unlock();
|
||||||
}
|
}
|
||||||
|
|
@ -3955,6 +3977,9 @@ static int pictureflow_main(const char* selected_file)
|
||||||
pf_state = pf_idle;
|
pf_state = pf_idle;
|
||||||
|
|
||||||
pf_tracks.cur_idx = -1;
|
pf_tracks.cur_idx = -1;
|
||||||
|
pf_tracks.borrowed = 0;
|
||||||
|
pf_tracks.used = 0;
|
||||||
|
|
||||||
extra_fade = 0;
|
extra_fade = 0;
|
||||||
slide_frame = 0;
|
slide_frame = 0;
|
||||||
step = 0;
|
step = 0;
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue