summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAidan MacDonald <amachronic@protonmail.com>2023-01-16 18:06:26 +0000
committerAidan MacDonald <amachronic@protonmail.com>2023-01-22 13:47:50 +0000
commitc307d98e3fbe5e867cd6e6e3c61f4937021c6b4c (patch)
tree3ae7d5b34a6c6f72a2dd50737dfe278db934446a
parent32f365bf3cdcaadb22087041bfc44574737b557d (diff)
downloadrockbox-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.c136
-rw-r--r--apps/playlist.h2
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
171static struct playlist_info current_playlist; 171static struct playlist_info current_playlist;
172 172
173static 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;
179static struct queue_sender_list playlist_queue_sender_list SHAREDBSS_ATTR; 198static struct queue_sender_list playlist_queue_sender_list SHAREDBSS_ATTR;
180static long playlist_stack[(DEFAULT_STACK_SIZE + 0x800)/sizeof(long)]; 199static long playlist_stack[(DEFAULT_STACK_SIZE + 0x800)/sizeof(long)];
181static const char dc_thread_playlist_name[] = "playlist cachectrl"; 200static const char dc_thread_playlist_name[] = "playlist cachectrl";
182
183static 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
1863static 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
1880static void dc_flush_playlist_callback(void) 1887static 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 = &current_playlist; 2052 struct playlist_info* playlist = &current_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 = &current_playlist; 2528 playlist = &current_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(&current_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 */