diff options
author | Aidan MacDonald <amachronic@protonmail.com> | 2023-01-16 18:06:26 +0000 |
---|---|---|
committer | Aidan MacDonald <amachronic@protonmail.com> | 2023-01-22 13:47:50 +0000 |
commit | c307d98e3fbe5e867cd6e6e3c61f4937021c6b4c (patch) | |
tree | 3ae7d5b34a6c6f72a2dd50737dfe278db934446a | |
parent | 32f365bf3cdcaadb22087041bfc44574737b557d (diff) | |
download | rockbox-c307d98e3fbe5e867cd6e6e3c61f4937021c6b4c.tar.gz rockbox-c307d98e3fbe5e867cd6e6e3c61f4937021c6b4c.zip |
playlist: pin dircache fileref buffer during background scanning
dircache_search() can yield, which would lead to memory corruption
if the playlist dcfrefs buffer is moved at that point. Prevent this
from happening by storing the buflib handle and pinning the buffer
while scanning the dircache.
Change-Id: I28b122de283953dd6d54c1d00598759f5bdcbe93
-rw-r--r-- | apps/playlist.c | 136 | ||||
-rw-r--r-- | apps/playlist.h | 2 |
2 files changed, 68 insertions, 70 deletions
diff --git a/apps/playlist.c b/apps/playlist.c index b56959fefc..7bdcafa69a 100644 --- a/apps/playlist.c +++ b/apps/playlist.c | |||
@@ -170,6 +170,25 @@ struct directory_search_context { | |||
170 | 170 | ||
171 | static struct playlist_info current_playlist; | 171 | static struct playlist_info current_playlist; |
172 | 172 | ||
173 | static void dc_init_filerefs(struct playlist_info *playlist, | ||
174 | int start, int count) | ||
175 | { | ||
176 | #ifdef HAVE_DIRCACHE | ||
177 | if (!playlist->dcfrefs_handle) | ||
178 | return; | ||
179 | |||
180 | struct dircache_fileref *dcfrefs = core_get_data(playlist->dcfrefs_handle); | ||
181 | int end = start + count; | ||
182 | |||
183 | for (int i = start; i < end; i++) | ||
184 | dircache_fileref_init(&dcfrefs[i]); | ||
185 | #else | ||
186 | (void)playlist; | ||
187 | (void)start; | ||
188 | (void)count; | ||
189 | #endif | ||
190 | } | ||
191 | |||
173 | #ifdef HAVE_DIRCACHE | 192 | #ifdef HAVE_DIRCACHE |
174 | #define PLAYLIST_LOAD_POINTERS 1 | 193 | #define PLAYLIST_LOAD_POINTERS 1 |
175 | #define PLAYLIST_CLEAN_POINTERS 2 | 194 | #define PLAYLIST_CLEAN_POINTERS 2 |
@@ -179,9 +198,6 @@ static struct event_queue playlist_queue SHAREDBSS_ATTR; | |||
179 | static struct queue_sender_list playlist_queue_sender_list SHAREDBSS_ATTR; | 198 | static struct queue_sender_list playlist_queue_sender_list SHAREDBSS_ATTR; |
180 | static long playlist_stack[(DEFAULT_STACK_SIZE + 0x800)/sizeof(long)]; | 199 | static long playlist_stack[(DEFAULT_STACK_SIZE + 0x800)/sizeof(long)]; |
181 | static const char dc_thread_playlist_name[] = "playlist cachectrl"; | 200 | static const char dc_thread_playlist_name[] = "playlist cachectrl"; |
182 | |||
183 | static void dc_copy_filerefs(struct dircache_fileref*, | ||
184 | const struct dircache_fileref*, int); | ||
185 | #endif | 201 | #endif |
186 | 202 | ||
187 | #if defined(PLAYLIST_DEBUG_MUTEX) | 203 | #if defined(PLAYLIST_DEBUG_MUTEX) |
@@ -882,9 +898,7 @@ static int add_indices_to_playlist(struct playlist_info* playlist, | |||
882 | 898 | ||
883 | /* Store a new entry */ | 899 | /* Store a new entry */ |
884 | playlist->indices[ playlist->amount ] = i+count; | 900 | playlist->indices[ playlist->amount ] = i+count; |
885 | #ifdef HAVE_DIRCACHE | 901 | dc_init_filerefs(playlist, playlist->amount, 1); |
886 | dc_copy_filerefs(&playlist->dcfrefs[playlist->amount], NULL, 1); | ||
887 | #endif | ||
888 | playlist->amount++; | 902 | playlist->amount++; |
889 | } | 903 | } |
890 | } | 904 | } |
@@ -1191,13 +1205,14 @@ static int get_track_filename(struct playlist_info* playlist, int index, int see | |||
1191 | buf_length = MAX_PATH+1; | 1205 | buf_length = MAX_PATH+1; |
1192 | 1206 | ||
1193 | #ifdef HAVE_DIRCACHE | 1207 | #ifdef HAVE_DIRCACHE |
1194 | if (playlist->dcfrefs) | 1208 | if (playlist->dcfrefs_handle) |
1195 | { | 1209 | { |
1196 | max = dircache_get_fileref_path(&playlist->dcfrefs[index], | 1210 | struct dircache_fileref *dcfrefs = core_get_data_pinned(playlist->dcfrefs_handle); |
1211 | max = dircache_get_fileref_path(&dcfrefs[index], | ||
1197 | tmp_buf, sizeof(tmp_buf)); | 1212 | tmp_buf, sizeof(tmp_buf)); |
1198 | 1213 | ||
1199 | NOTEF("%s [in DCache]: 0x%x %s", __func__, | 1214 | NOTEF("%s [in DCache]: 0x%x %s", __func__, dcfrefs[index], tmp_buf); |
1200 | playlist->dcfrefs[index], tmp_buf); | 1215 | core_put_data_pinned(dcfrefs); |
1201 | } | 1216 | } |
1202 | #endif /* HAVE_DIRCACHE */ | 1217 | #endif /* HAVE_DIRCACHE */ |
1203 | 1218 | ||
@@ -1425,14 +1440,20 @@ static int add_track_to_playlist_unlocked(struct playlist_info* playlist, | |||
1425 | if (queue) | 1440 | if (queue) |
1426 | flags |= PLAYLIST_QUEUED; | 1441 | flags |= PLAYLIST_QUEUED; |
1427 | 1442 | ||
1443 | #ifdef HAVE_DIRCACHE | ||
1444 | struct dircache_fileref *dcfrefs = NULL; | ||
1445 | if (playlist->dcfrefs_handle) | ||
1446 | dcfrefs = core_get_data(playlist->dcfrefs_handle); | ||
1447 | #else | ||
1448 | int *dcfrefs = NULL; | ||
1449 | #endif | ||
1450 | |||
1428 | /* shift indices so that track can be added */ | 1451 | /* shift indices so that track can be added */ |
1429 | for (i=playlist->amount; i>insert_position; i--) | 1452 | for (i=playlist->amount; i>insert_position; i--) |
1430 | { | 1453 | { |
1431 | playlist->indices[i] = playlist->indices[i-1]; | 1454 | playlist->indices[i] = playlist->indices[i-1]; |
1432 | #ifdef HAVE_DIRCACHE | 1455 | if (dcfrefs) |
1433 | if (playlist->dcfrefs) | 1456 | dcfrefs[i] = dcfrefs[i-1]; |
1434 | playlist->dcfrefs[i] = playlist->dcfrefs[i-1]; | ||
1435 | #endif | ||
1436 | } | 1457 | } |
1437 | 1458 | ||
1438 | /* update stored indices if needed */ | 1459 | /* update stored indices if needed */ |
@@ -1463,10 +1484,7 @@ static int add_track_to_playlist_unlocked(struct playlist_info* playlist, | |||
1463 | } | 1484 | } |
1464 | 1485 | ||
1465 | playlist->indices[insert_position] = flags | seek_pos; | 1486 | playlist->indices[insert_position] = flags | seek_pos; |
1466 | 1487 | dc_init_filerefs(playlist, insert_position, 1); | |
1467 | #ifdef HAVE_DIRCACHE | ||
1468 | dc_copy_filerefs(&playlist->dcfrefs[insert_position], NULL, 1); | ||
1469 | #endif | ||
1470 | 1488 | ||
1471 | playlist->amount++; | 1489 | playlist->amount++; |
1472 | playlist->num_inserted_tracks++; | 1490 | playlist->num_inserted_tracks++; |
@@ -1534,14 +1552,20 @@ static int remove_track_from_playlist(struct playlist_info* playlist, | |||
1534 | 1552 | ||
1535 | inserted = playlist->indices[position] & PLAYLIST_INSERT_TYPE_MASK; | 1553 | inserted = playlist->indices[position] & PLAYLIST_INSERT_TYPE_MASK; |
1536 | 1554 | ||
1555 | #ifdef HAVE_DIRCACHE | ||
1556 | struct dircache_fileref *dcfrefs = NULL; | ||
1557 | if (playlist->dcfrefs_handle) | ||
1558 | dcfrefs = core_get_data(playlist->dcfrefs_handle); | ||
1559 | #else | ||
1560 | int *dcfrefs = NULL; | ||
1561 | #endif | ||
1562 | |||
1537 | /* shift indices now that track has been removed */ | 1563 | /* shift indices now that track has been removed */ |
1538 | for (i=position; i<playlist->amount; i++) | 1564 | for (i=position; i<playlist->amount; i++) |
1539 | { | 1565 | { |
1540 | playlist->indices[i] = playlist->indices[i+1]; | 1566 | playlist->indices[i] = playlist->indices[i+1]; |
1541 | #ifdef HAVE_DIRCACHE | 1567 | if (dcfrefs) |
1542 | if (playlist->dcfrefs) | 1568 | dcfrefs[i] = dcfrefs[i+1]; |
1543 | playlist->dcfrefs[i] = playlist->dcfrefs[i+1]; | ||
1544 | #endif | ||
1545 | } | 1569 | } |
1546 | 1570 | ||
1547 | playlist->amount--; | 1571 | playlist->amount--; |
@@ -1630,11 +1654,12 @@ static int randomise_playlist(struct playlist_info* playlist, | |||
1630 | playlist->indices[candidate] = playlist->indices[count]; | 1654 | playlist->indices[candidate] = playlist->indices[count]; |
1631 | playlist->indices[count] = indextmp; | 1655 | playlist->indices[count] = indextmp; |
1632 | #ifdef HAVE_DIRCACHE | 1656 | #ifdef HAVE_DIRCACHE |
1633 | if (playlist->dcfrefs) | 1657 | if (playlist->dcfrefs_handle) |
1634 | { | 1658 | { |
1635 | struct dircache_fileref dcftmp = playlist->dcfrefs[candidate]; | 1659 | struct dircache_fileref *dcfrefs = core_get_data(playlist->dcfrefs_handle); |
1636 | playlist->dcfrefs[candidate] = playlist->dcfrefs[count]; | 1660 | struct dircache_fileref dcftmp = dcfrefs[candidate]; |
1637 | playlist->dcfrefs[count] = dcftmp; | 1661 | dcfrefs[candidate] = dcfrefs[count]; |
1662 | dcfrefs[count] = dcftmp; | ||
1638 | } | 1663 | } |
1639 | #endif | 1664 | #endif |
1640 | } | 1665 | } |
@@ -1707,7 +1732,7 @@ static int sort_playlist_unlocked(struct playlist_info* playlist, | |||
1707 | /** We need to re-check the song names from disk because qsort can't | 1732 | /** We need to re-check the song names from disk because qsort can't |
1708 | * sort two arrays at once :/ | 1733 | * sort two arrays at once :/ |
1709 | * FIXME: Please implement a better way to do this. */ | 1734 | * FIXME: Please implement a better way to do this. */ |
1710 | dc_copy_filerefs(playlist->dcfrefs, NULL, playlist->max_playlist_size); | 1735 | dc_init_filerefs(playlist, 0, playlist->max_playlist_size); |
1711 | dc_load_playlist_pointers(); | 1736 | dc_load_playlist_pointers(); |
1712 | #endif | 1737 | #endif |
1713 | 1738 | ||
@@ -1859,24 +1884,6 @@ static int get_next_index(const struct playlist_info* playlist, int steps, | |||
1859 | } | 1884 | } |
1860 | 1885 | ||
1861 | #ifdef HAVE_DIRCACHE | 1886 | #ifdef HAVE_DIRCACHE |
1862 | |||
1863 | static void dc_copy_filerefs(struct dircache_fileref *dcfto, | ||
1864 | const struct dircache_fileref *dcffrom, | ||
1865 | int count) | ||
1866 | { | ||
1867 | if (!dcfto) | ||
1868 | return; | ||
1869 | |||
1870 | if (dcffrom) | ||
1871 | memmove(dcfto, dcffrom, count * sizeof (*dcfto)); | ||
1872 | else | ||
1873 | { | ||
1874 | /* just initialize the destination */ | ||
1875 | for (int i = 0; i < count; i++, dcfto++) | ||
1876 | dircache_fileref_init(dcfto); | ||
1877 | } | ||
1878 | } | ||
1879 | |||
1880 | static void dc_flush_playlist_callback(void) | 1887 | static void dc_flush_playlist_callback(void) |
1881 | { | 1888 | { |
1882 | struct playlist_info *playlist; | 1889 | struct playlist_info *playlist; |
@@ -1901,6 +1908,7 @@ static void dc_thread_playlist(void) | |||
1901 | static char tmp[MAX_PATH+1]; | 1908 | static char tmp[MAX_PATH+1]; |
1902 | 1909 | ||
1903 | struct playlist_info *playlist; | 1910 | struct playlist_info *playlist; |
1911 | struct dircache_fileref *dcfrefs; | ||
1904 | int index; | 1912 | int index; |
1905 | int seek; | 1913 | int seek; |
1906 | bool control_file; | 1914 | bool control_file; |
@@ -1939,7 +1947,7 @@ static void dc_thread_playlist(void) | |||
1939 | register_storage_idle_func(dc_flush_playlist_callback); | 1947 | register_storage_idle_func(dc_flush_playlist_callback); |
1940 | } | 1948 | } |
1941 | 1949 | ||
1942 | if (!playlist->dcfrefs || playlist->amount <= 0) | 1950 | if (!playlist->dcfrefs_handle || playlist->amount <= 0) |
1943 | break ; | 1951 | break ; |
1944 | 1952 | ||
1945 | /* Check if previously loaded pointers are intact. */ | 1953 | /* Check if previously loaded pointers are intact. */ |
@@ -1953,12 +1961,13 @@ static void dc_thread_playlist(void) | |||
1953 | break ; | 1961 | break ; |
1954 | 1962 | ||
1955 | trigger_cpu_boost(); | 1963 | trigger_cpu_boost(); |
1964 | dcfrefs = core_get_data_pinned(playlist->dcfrefs_handle); | ||
1956 | 1965 | ||
1957 | for (index = 0; index < playlist->amount | 1966 | for (index = 0; index < playlist->amount |
1958 | && queue_empty(&playlist_queue); index++) | 1967 | && queue_empty(&playlist_queue); index++) |
1959 | { | 1968 | { |
1960 | /* Process only pointers that are superficially stale. */ | 1969 | /* Process only pointers that are superficially stale. */ |
1961 | if (dircache_search(DCS_FILEREF, &playlist->dcfrefs[index], NULL) > 0) | 1970 | if (dircache_search(DCS_FILEREF, &dcfrefs[index], NULL) > 0) |
1962 | continue ; | 1971 | continue ; |
1963 | 1972 | ||
1964 | control_file = playlist->indices[index] & PLAYLIST_INSERT_TYPE_MASK; | 1973 | control_file = playlist->indices[index] & PLAYLIST_INSERT_TYPE_MASK; |
@@ -1973,12 +1982,13 @@ static void dc_thread_playlist(void) | |||
1973 | 1982 | ||
1974 | /* Obtain the dircache file entry cookie. */ | 1983 | /* Obtain the dircache file entry cookie. */ |
1975 | dircache_search(DCS_CACHED_PATH | DCS_UPDATE_FILEREF, | 1984 | dircache_search(DCS_CACHED_PATH | DCS_UPDATE_FILEREF, |
1976 | &playlist->dcfrefs[index], tmp); | 1985 | &dcfrefs[index], tmp); |
1977 | 1986 | ||
1978 | /* And be on background so user doesn't notice any delays. */ | 1987 | /* And be on background so user doesn't notice any delays. */ |
1979 | yield(); | 1988 | yield(); |
1980 | } | 1989 | } |
1981 | 1990 | ||
1991 | core_put_data_pinned(dcfrefs); | ||
1982 | cancel_cpu_boost(); | 1992 | cancel_cpu_boost(); |
1983 | 1993 | ||
1984 | if (index == playlist->amount) | 1994 | if (index == playlist->amount) |
@@ -2042,10 +2052,6 @@ static int move_callback(int handle, void* current, void* new) | |||
2042 | struct playlist_info* playlist = ¤t_playlist; | 2052 | struct playlist_info* playlist = ¤t_playlist; |
2043 | if (current == playlist->indices) | 2053 | if (current == playlist->indices) |
2044 | playlist->indices = new; | 2054 | playlist->indices = new; |
2045 | #ifdef HAVE_DIRCACHE | ||
2046 | else if (current == playlist->dcfrefs) | ||
2047 | playlist->dcfrefs = new; | ||
2048 | #endif /* HAVE_DIRCACHE */ | ||
2049 | return BUFLIB_CB_OK; | 2055 | return BUFLIB_CB_OK; |
2050 | } | 2056 | } |
2051 | 2057 | ||
@@ -2083,10 +2089,10 @@ void playlist_init(void) | |||
2083 | initalize_new_playlist(playlist, true); | 2089 | initalize_new_playlist(playlist, true); |
2084 | 2090 | ||
2085 | #ifdef HAVE_DIRCACHE | 2091 | #ifdef HAVE_DIRCACHE |
2086 | handle = core_alloc_ex( | 2092 | playlist->dcfrefs_handle = core_alloc( |
2087 | playlist->max_playlist_size * sizeof(*playlist->dcfrefs), &ops); | 2093 | playlist->max_playlist_size * sizeof(struct dircache_fileref)); |
2088 | playlist->dcfrefs = core_get_data(handle); | 2094 | dc_init_filerefs(playlist, 0, playlist->max_playlist_size); |
2089 | dc_copy_filerefs(playlist->dcfrefs, NULL, playlist->max_playlist_size); | 2095 | |
2090 | unsigned int playlist_thread_id = | 2096 | unsigned int playlist_thread_id = |
2091 | create_thread(dc_thread_playlist, playlist_stack, sizeof(playlist_stack), | 2097 | create_thread(dc_thread_playlist, playlist_stack, sizeof(playlist_stack), |
2092 | 0, dc_thread_playlist_name IF_PRIO(, PRIORITY_BACKGROUND) | 2098 | 0, dc_thread_playlist_name IF_PRIO(, PRIORITY_BACKGROUND) |
@@ -2149,10 +2155,7 @@ int playlist_add(const char *filename) | |||
2149 | chunk_put_data(&playlist->name_chunk_buffer, namebuf, indice); | 2155 | chunk_put_data(&playlist->name_chunk_buffer, namebuf, indice); |
2150 | 2156 | ||
2151 | playlist->indices[playlist->amount] = indice; | 2157 | playlist->indices[playlist->amount] = indice; |
2152 | 2158 | dc_init_filerefs(playlist, playlist->amount, 1); | |
2153 | #ifdef HAVE_DIRCACHE | ||
2154 | dc_copy_filerefs(&playlist->dcfrefs[playlist->amount], NULL, 1); | ||
2155 | #endif | ||
2156 | 2159 | ||
2157 | playlist->amount++; | 2160 | playlist->amount++; |
2158 | 2161 | ||
@@ -2217,15 +2220,16 @@ int playlist_create_ex(struct playlist_info* playlist, | |||
2217 | playlist->max_playlist_size = num_indices; | 2220 | playlist->max_playlist_size = num_indices; |
2218 | playlist->indices = index_buffer; | 2221 | playlist->indices = index_buffer; |
2219 | #ifdef HAVE_DIRCACHE | 2222 | #ifdef HAVE_DIRCACHE |
2220 | playlist->dcfrefs = (void *)&playlist->indices[num_indices]; | 2223 | playlist->dcfrefs_handle = 0; |
2221 | #endif | 2224 | #endif |
2222 | } | 2225 | } |
2223 | else | 2226 | else |
2224 | { | 2227 | { |
2228 | /* FIXME not sure if it's safe to share index buffers */ | ||
2225 | playlist->max_playlist_size = current_playlist.max_playlist_size; | 2229 | playlist->max_playlist_size = current_playlist.max_playlist_size; |
2226 | playlist->indices = current_playlist.indices; | 2230 | playlist->indices = current_playlist.indices; |
2227 | #ifdef HAVE_DIRCACHE | 2231 | #ifdef HAVE_DIRCACHE |
2228 | playlist->dcfrefs = current_playlist.dcfrefs; | 2232 | playlist->dcfrefs_handle = 0; |
2229 | #endif | 2233 | #endif |
2230 | } | 2234 | } |
2231 | 2235 | ||
@@ -2524,9 +2528,6 @@ size_t playlist_get_required_bufsz(struct playlist_info* playlist, | |||
2524 | playlist = ¤t_playlist; | 2528 | playlist = ¤t_playlist; |
2525 | 2529 | ||
2526 | size_t unit_size = sizeof (*playlist->indices); | 2530 | size_t unit_size = sizeof (*playlist->indices); |
2527 | #ifdef HAVE_DIRCACHE | ||
2528 | unit_size += sizeof (*playlist->dcfrefs); | ||
2529 | #endif | ||
2530 | if (include_namebuf) | 2531 | if (include_namebuf) |
2531 | namebuf = AVERAGE_FILENAME_LENGTH * global_settings.max_files_in_dir; | 2532 | namebuf = AVERAGE_FILENAME_LENGTH * global_settings.max_files_in_dir; |
2532 | 2533 | ||
@@ -3909,10 +3910,7 @@ int playlist_set_current(struct playlist_info* playlist) | |||
3909 | { | 3910 | { |
3910 | memcpy((void*)current_playlist.indices, (void*)playlist->indices, | 3911 | memcpy((void*)current_playlist.indices, (void*)playlist->indices, |
3911 | playlist->max_playlist_size*sizeof(*playlist->indices)); | 3912 | playlist->max_playlist_size*sizeof(*playlist->indices)); |
3912 | #ifdef HAVE_DIRCACHE | 3913 | dc_init_filerefs(¤t_playlist, 0, current_playlist.max_playlist_size); |
3913 | dc_copy_filerefs(current_playlist.dcfrefs, playlist->dcfrefs, | ||
3914 | playlist->max_playlist_size); | ||
3915 | #endif | ||
3916 | } | 3914 | } |
3917 | 3915 | ||
3918 | current_playlist.first_index = playlist->first_index; | 3916 | current_playlist.first_index = playlist->first_index; |
diff --git a/apps/playlist.h b/apps/playlist.h index ef943f1bd9..86c0c293ab 100644 --- a/apps/playlist.h +++ b/apps/playlist.h | |||
@@ -107,7 +107,7 @@ struct playlist_info | |||
107 | int num_cached; /* number of cached entries */ | 107 | int num_cached; /* number of cached entries */ |
108 | struct mutex mutex; /* mutex for control file access */ | 108 | struct mutex mutex; /* mutex for control file access */ |
109 | #ifdef HAVE_DIRCACHE | 109 | #ifdef HAVE_DIRCACHE |
110 | struct dircache_fileref *dcfrefs; /* Dircache entry shortcuts */ | 110 | int dcfrefs_handle; |
111 | #endif | 111 | #endif |
112 | int dirlen; /* Length of the path to the playlist file */ | 112 | int dirlen; /* Length of the path to the playlist file */ |
113 | char filename[MAX_PATH]; /* path name of m3u playlist on disk */ | 113 | char filename[MAX_PATH]; /* path name of m3u playlist on disk */ |