Skip to content

Commit 27c6a35

Browse files
alepachecotillkamppeter
authored andcommitted
Swap the PPD cache status message under a lock
set_error() freed ppd_cache_status_message and then stored the new message, so two threads failing in ppdCacheCreateWithFile() or ppdCacheWriteFile() at the same time could both free the same pooled string. Build the new message first, swap it in under a mutex and free the previous one afterwards. Extend test_ppd_thread with concurrent ppdCacheCreateWithFile() failures.
1 parent a607e7a commit 27c6a35

2 files changed

Lines changed: 85 additions & 13 deletions

File tree

‎ppd/ppd-cache.c‎

Lines changed: 23 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
//
1414

1515
#include <ppd/string-private.h>
16+
#include <ppd/thread-private.h>
1617
#include <ppd/array-private.h>
1718
#include <ppd/ipp-private.h>
1819
#include <ppd/ppd.h>
@@ -76,6 +77,8 @@ static int ppd_pwg_compare_finishings(ppd_pwg_finishings_t *a,
7677
static void ppd_pwg_free_finishings(ppd_pwg_finishings_t *f);
7778

7879
char *ppd_cache_status_message = NULL; // Last PPD cache error
80+
static _ppd_mutex_t ppd_cache_status_mutex = _PPD_MUTEX_INITIALIZER;
81+
// Mutex to control access to it
7982

8083

8184
//
@@ -86,19 +89,16 @@ static void
8689
set_error(const char *message, // I - status-message value
8790
int localize) // I - Localize the message?
8891
{
92+
char *new_message = NULL, // New status-message
93+
*old_message; // Previous status-message
94+
95+
8996
if (!message && errno)
9097
{
9198
message = strerror(errno);
9299
localize = 0;
93100
}
94101

95-
if (ppd_cache_status_message)
96-
{
97-
_ppdStrFree(ppd_cache_status_message);
98-
99-
ppd_cache_status_message = NULL;
100-
}
101-
102102
if (message)
103103
{
104104
if (localize)
@@ -107,16 +107,26 @@ set_error(const char *message, // I - status-message value
107107
// Get the message catalog...
108108
//
109109

110-
ppd_cache_status_message =
111-
_ppdStrAlloc(cupsLangGetString(cupsLangDefault(),
112-
message));
110+
new_message = _ppdStrAlloc(cupsLangGetString(cupsLangDefault(),
111+
message));
113112
}
114113
else
115-
ppd_cache_status_message = _ppdStrAlloc(message);
114+
new_message = _ppdStrAlloc(message);
116115
}
117116

118-
DEBUG_printf(("4set_error: last_status_message=\"%s\"",
119-
ppd_cache_status_message));
117+
DEBUG_printf(("4set_error: last_status_message=\"%s\"", new_message));
118+
119+
//
120+
// Swap in the new message and free the previous one, so that threads
121+
// setting errors at the same time never free the same string twice...
122+
//
123+
124+
_ppdMutexLock(&ppd_cache_status_mutex);
125+
old_message = ppd_cache_status_message;
126+
ppd_cache_status_message = new_message;
127+
_ppdMutexUnlock(&ppd_cache_status_mutex);
128+
129+
_ppdStrFree(old_message);
120130
}
121131

122132
//

‎ppd/test_ppd_thread.c‎

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,10 @@
3737
// loop, each with its own code, and must read back exactly the
3838
// message the same code gives when run alone.
3939
//
40+
// Group 4 PPD cache status message: several threads call
41+
// ppdCacheCreateWithFile() on a file that does not exist in a
42+
// loop, each failure replacing the last status message.
43+
//
4044
// The per-thread checks of groups 1 and 3 fail deterministically when the
4145
// state is shared by all threads. The concurrent checks may crash or pass
4246
// by luck then; build with -fsanitize=thread to see every data race they run
@@ -107,6 +111,7 @@ static const char test_ppd_text[] =
107111
static int join_threads(pthread_t *threads, int started,
108112
thread_data_t *data, int *loops);
109113
static ppd_file_t *open_ppd(const char *text);
114+
static void *run_cache_status(void *arg);
110115
static void *run_open_ppd(void *arg);
111116
static void *run_raster_concurrent(void *arg);
112117
static void *run_raster_new_thread(void *arg);
@@ -115,6 +120,7 @@ static void set_code(thread_data_t *data, int num);
115120
static int set_expected(thread_data_t *data);
116121
static int start_threads(pthread_t *threads, thread_data_t *data,
117122
void *(*func)(void *));
123+
static void test_cache_status(void);
118124
static void test_ppd_status(void);
119125
static void test_raster_error(void);
120126
static void test_string_pool(void);
@@ -130,6 +136,7 @@ main(void)
130136
test_ppd_status();
131137
test_string_pool();
132138
test_raster_error();
139+
test_cache_status();
133140

134141
return (testsPassed ? 0 : 1);
135142
}
@@ -185,6 +192,32 @@ open_ppd(const char *text) // I - PPD text
185192
}
186193

187194

195+
//
196+
// 'run_cache_status()' - Fail to load a PPD cache file in a loop.
197+
//
198+
199+
static void * // O - Thread exit value
200+
run_cache_status(void *arg) // I - Thread data
201+
{
202+
thread_data_t *data = (thread_data_t *)arg;
203+
// Thread data
204+
ppd_cache_t *pc; // PPD cache
205+
206+
207+
for (data->loops = 0; data->loops < NUM_LOOPS; data->loops ++)
208+
{
209+
if ((pc = ppdCacheCreateWithFile("/nonexistent/libppd-test.cache",
210+
NULL)) != NULL)
211+
{
212+
ppdCacheDestroy(pc);
213+
data->mismatches ++;
214+
}
215+
}
216+
217+
return (NULL);
218+
}
219+
220+
188221
//
189222
// 'run_open_ppd()' - Open a valid PPD and check its status.
190223
//
@@ -413,6 +446,35 @@ start_threads(pthread_t *threads, // I - Threads
413446
}
414447

415448

449+
//
450+
// 'test_cache_status()' - Test the PPD cache status message (group 4).
451+
//
452+
453+
static void
454+
test_cache_status(void)
455+
{
456+
pthread_t threads[NUM_THREADS]; // Threads
457+
thread_data_t data[NUM_THREADS]; // Per-thread test data
458+
int i, // Looping var
459+
started, // Threads started
460+
loops, // Iterations done
461+
mismatches; // Mismatches seen
462+
463+
464+
memset(data, 0, sizeof(data));
465+
for (i = 0; i < NUM_THREADS; i ++)
466+
data[i].num = i;
467+
468+
testBegin("ppdCacheCreateWithFile(missing file) in %d threads x %d loops",
469+
NUM_THREADS, NUM_LOOPS);
470+
started = start_threads(threads, data, run_cache_status);
471+
mismatches = join_threads(threads, started, data, &loops);
472+
testEndMessage(started == NUM_THREADS && mismatches == 0,
473+
"%d threads, %d of %d calls did not fail", started,
474+
mismatches, loops);
475+
}
476+
477+
416478
//
417479
// 'test_ppd_status()' - Test the status of ppdOpen() (group 1).
418480
//

0 commit comments

Comments
 (0)