Coverage Report

Created: 2023-03-26 06:28

/src/httpd/server/util_mutex.c
Line
Count
Source (jump to first uncovered line)
1
/* Licensed to the Apache Software Foundation (ASF) under one or more
2
 * contributor license agreements.  See the NOTICE file distributed with
3
 * this work for additional information regarding copyright ownership.
4
 * The ASF licenses this file to You under the Apache License, Version 2.0
5
 * (the "License"); you may not use this file except in compliance with
6
 * the License.  You may obtain a copy of the License at
7
 *
8
 *     http://www.apache.org/licenses/LICENSE-2.0
9
 *
10
 * Unless required by applicable law or agreed to in writing, software
11
 * distributed under the License is distributed on an "AS IS" BASIS,
12
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
 * See the License for the specific language governing permissions and
14
 * limitations under the License.
15
 */
16
17
/*
18
 * util_mutex.c: Useful functions for determining allowable
19
 *               mutexes and mutex settings
20
 */
21
22
23
#include "apr.h"
24
#include "apr_hash.h"
25
#include "apr_strings.h"
26
#include "apr_lib.h"
27
28
#define APR_WANT_STRFUNC
29
#include "apr_want.h"
30
31
#include "ap_config.h"
32
#include "httpd.h"
33
#include "http_main.h"
34
#include "http_config.h"
35
#include "http_core.h"
36
#include "http_log.h"
37
#include "util_mutex.h"
38
#if AP_NEED_SET_MUTEX_PERMS
39
#include "unixd.h"
40
#endif
41
#ifdef HAVE_UNISTD_H
42
#include <unistd.h> /* getpid() */
43
#endif
44
45
/* we know core's module_index is 0 */
46
#undef APLOG_MODULE_INDEX
47
#define APLOG_MODULE_INDEX AP_CORE_MODULE_INDEX
48
49
AP_DECLARE(apr_status_t) ap_parse_mutex(const char *arg, apr_pool_t *pool,
50
                                        apr_lockmech_e *mutexmech,
51
                                        const char **mutexfile)
52
0
{
53
    /* Split arg into meth and file */
54
0
    char *meth = apr_pstrdup(pool, arg);
55
0
    char *file = strchr(meth, ':');
56
0
    if (file) {
57
0
        *(file++) = '\0';
58
0
        if (!*file) {
59
0
            file = NULL;
60
0
        }
61
0
    }
62
63
    /* APR determines temporary filename unless overridden below,
64
     * we presume file indicates an mutexfile is a file path
65
     * unless the method sets mutexfile=file and NULLs file
66
     */
67
0
    *mutexfile = NULL;
68
69
0
    if (!strcasecmp(meth, "none") || !strcasecmp(meth, "no")) {
70
0
        return APR_ENOLOCK;
71
0
    }
72
73
    /* NOTE: previously, 'yes' implied 'sem' */
74
0
    if (!strcasecmp(meth, "default") || !strcasecmp(meth, "yes")) {
75
0
        *mutexmech = APR_LOCK_DEFAULT;
76
0
    }
77
0
#if APR_HAS_FCNTL_SERIALIZE
78
0
    else if (!strcasecmp(meth, "fcntl") || !strcasecmp(meth, "file")) {
79
0
        *mutexmech = APR_LOCK_FCNTL;
80
0
    }
81
0
#endif
82
0
#if APR_HAS_FLOCK_SERIALIZE
83
0
    else if (!strcasecmp(meth, "flock") || !strcasecmp(meth, "file")) {
84
0
        *mutexmech = APR_LOCK_FLOCK;
85
0
    }
86
0
#endif
87
0
#if APR_HAS_POSIXSEM_SERIALIZE
88
0
    else if (!strcasecmp(meth, "posixsem") || !strcasecmp(meth, "sem")) {
89
0
        *mutexmech = APR_LOCK_POSIXSEM;
90
        /* Posix/SysV semaphores aren't file based, use the literal name
91
         * if provided and fall back on APR's default if not.  Today, APR
92
         * will ignore it, but once supported it has an absurdly short limit.
93
         */
94
0
        if (file) {
95
0
            *mutexfile = apr_pstrdup(pool, file);
96
97
0
            file = NULL;
98
0
        }
99
0
    }
100
0
#endif
101
0
#if APR_HAS_SYSVSEM_SERIALIZE
102
0
    else if (!strcasecmp(meth, "sysvsem") || !strcasecmp(meth, "sem")) {
103
0
        *mutexmech = APR_LOCK_SYSVSEM;
104
0
    }
105
0
#endif
106
0
#if APR_HAS_PROC_PTHREAD_SERIALIZE
107
0
    else if (!strcasecmp(meth, "pthread")) {
108
0
        *mutexmech = APR_LOCK_PROC_PTHREAD;
109
0
    }
110
0
#endif
111
0
    else {
112
0
        return APR_ENOTIMPL;
113
0
    }
114
115
    /* Unless the method above assumed responsibility for setting up
116
     * mutexfile and NULLing out file, presume it is a file we
117
     * are looking to use
118
     */
119
0
    if (file) {
120
0
        *mutexfile = ap_runtime_dir_relative(pool, file);
121
0
        if (!*mutexfile) {
122
0
            return APR_BADARG;
123
0
        }
124
0
    }
125
126
0
    return APR_SUCCESS;
127
0
}
128
129
typedef struct {
130
    apr_int32_t options;
131
    unsigned int set :      1;
132
    unsigned int none :     1;
133
    unsigned int omit_pid : 1;
134
    apr_lockmech_e mech;
135
    const char *dir;
136
} mutex_cfg_t;
137
138
/* hash is created the first time a module calls ap_mutex_register(),
139
 * rather than attempting to be the REALLY_REALLY_FIRST pre-config
140
 * hook; it is cleaned up when the associated pool goes away; assume
141
 * pconf is the pool passed to ap_mutex_register()
142
 */
143
static apr_hash_t *mxcfg_by_type;
144
145
AP_DECLARE_NONSTD(void) ap_mutex_init(apr_pool_t *p)
146
0
{
147
0
    mutex_cfg_t *def;
148
149
0
    if (mxcfg_by_type) {
150
0
        return;
151
0
    }
152
153
0
    mxcfg_by_type = apr_hash_make(p);
154
0
    apr_pool_cleanup_register(p, &mxcfg_by_type, ap_pool_cleanup_set_null,
155
0
        apr_pool_cleanup_null);
156
157
    /* initialize default mutex configuration */
158
0
    def = apr_pcalloc(p, sizeof *def);
159
0
    def->mech = APR_LOCK_DEFAULT;
160
0
    def->dir = ap_runtime_dir_relative(p, "");
161
0
    apr_hash_set(mxcfg_by_type, "default", APR_HASH_KEY_STRING, def);
162
0
}
163
164
AP_DECLARE_NONSTD(const char *)ap_set_mutex(cmd_parms *cmd, void *dummy,
165
                                            const char *arg)
166
0
{
167
0
    apr_pool_t *p = cmd->pool;
168
0
    apr_pool_t *ptemp = cmd->temp_pool;
169
0
    const char **elt;
170
0
    const char *mechdir;
171
0
    int no_mutex = 0, omit_pid = 0;
172
0
    apr_array_header_t *type_list;
173
0
    apr_lockmech_e mech;
174
0
    apr_status_t rv;
175
0
    const char *mutexdir;
176
0
    mutex_cfg_t *mxcfg;
177
0
    const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
178
179
0
    if (err != NULL) {
180
0
        return err;
181
0
    }
182
183
0
    mechdir = ap_getword_conf(cmd->pool, &arg);
184
0
    if (*mechdir == '\0') {
185
0
        return "Mutex requires at least a mechanism argument ("
186
0
               AP_ALL_AVAILABLE_MUTEXES_STRING ")";
187
0
    }
188
189
0
    rv = ap_parse_mutex(mechdir, p, &mech, &mutexdir);
190
0
    if (rv == APR_ENOTIMPL) {
191
0
        return apr_pstrcat(p, "Invalid Mutex argument ", mechdir,
192
0
                           " (" AP_ALL_AVAILABLE_MUTEXES_STRING ")", NULL);
193
0
    }
194
0
    else if (rv == APR_BADARG
195
0
             || (mutexdir && !ap_is_directory(ptemp, mutexdir))) {
196
0
        return apr_pstrcat(p, "Invalid Mutex directory in argument ",
197
0
                           mechdir, NULL);
198
0
    }
199
0
    else if (rv == APR_ENOLOCK) { /* "none" */
200
0
        no_mutex = 1;
201
0
    }
202
203
    /* "OmitPID" can appear at the end of the list, so build a list of
204
     * mutex type names while looking for "OmitPID" (anywhere) or the end
205
     */
206
0
    type_list = apr_array_make(cmd->pool, 4, sizeof(const char *));
207
0
    while (*arg) {
208
0
        const char *s = ap_getword_conf(cmd->pool, &arg);
209
210
0
        if (!strcasecmp(s, "omitpid")) {
211
0
            omit_pid = 1;
212
0
        }
213
0
        else {
214
0
            const char **new_type = (const char **)apr_array_push(type_list);
215
0
            *new_type = s;
216
0
        }
217
0
    }
218
219
0
    if (apr_is_empty_array(type_list)) { /* no mutex type?  assume "default" */
220
0
        const char **new_type = (const char **)apr_array_push(type_list);
221
0
        *new_type = "default";
222
0
    }
223
224
0
    while ((elt = (const char **)apr_array_pop(type_list)) != NULL) {
225
0
        const char *type = *elt;
226
0
        mxcfg = apr_hash_get(mxcfg_by_type, type, APR_HASH_KEY_STRING);
227
0
        if (!mxcfg) {
228
0
            return apr_psprintf(p, "Mutex type %s is not valid", type);
229
0
        }
230
231
0
        mxcfg->none = 0; /* in case that was the default */
232
0
        mxcfg->omit_pid = omit_pid;
233
234
0
        mxcfg->set = 1;
235
0
        if (no_mutex) {
236
0
            if (!(mxcfg->options & AP_MUTEX_ALLOW_NONE)) {
237
0
                return apr_psprintf(p,
238
0
                                    "None is not allowed for mutex type %s",
239
0
                                    type);
240
0
            }
241
0
            mxcfg->none = 1;
242
0
        }
243
0
        else {
244
0
            mxcfg->mech = mech;
245
0
            if (mutexdir) { /* retain mutex default if not configured */
246
0
                mxcfg->dir = mutexdir;
247
0
            }
248
0
        }
249
0
    }
250
251
0
    return NULL;
252
0
}
253
254
AP_DECLARE(apr_status_t) ap_mutex_register(apr_pool_t *pconf,
255
                                           const char *type,
256
                                           const char *default_dir,
257
                                           apr_lockmech_e default_mech,
258
                                           apr_int32_t options)
259
0
{
260
0
    mutex_cfg_t *mxcfg = apr_pcalloc(pconf, sizeof *mxcfg);
261
262
0
    if ((options & ~(AP_MUTEX_ALLOW_NONE | AP_MUTEX_DEFAULT_NONE))) {
263
0
        return APR_EINVAL;
264
0
    }
265
266
0
    ap_mutex_init(pconf); /* in case this mod's pre-config ran before core's */
267
268
0
    mxcfg->options = options;
269
0
    if (options & AP_MUTEX_DEFAULT_NONE) {
270
0
        mxcfg->none = 1;
271
0
    }
272
0
    mxcfg->dir = default_dir; /* usually NULL */
273
0
    mxcfg->mech = default_mech; /* usually APR_LOCK_DEFAULT */
274
0
    apr_hash_set(mxcfg_by_type, type, APR_HASH_KEY_STRING, mxcfg);
275
276
0
    return APR_SUCCESS;
277
0
}
278
279
static int mutex_needs_file(apr_lockmech_e mech)
280
0
{
281
0
    if (mech != APR_LOCK_FLOCK
282
0
        && mech != APR_LOCK_FCNTL
283
#if APR_USE_FLOCK_SERIALIZE || APR_USE_FCNTL_SERIALIZE
284
        && mech != APR_LOCK_DEFAULT
285
#endif
286
0
        ) {
287
0
        return 0;
288
0
    }
289
0
    return 1;
290
0
}
291
292
static const char *get_mutex_filename(apr_pool_t *p, mutex_cfg_t *mxcfg,
293
                                      const char *type,
294
                                      const char *instance_id)
295
0
{
296
0
    const char *pid_suffix = "";
297
298
0
    if (!mutex_needs_file(mxcfg->mech)) {
299
0
        return NULL;
300
0
    }
301
302
0
#if HAVE_UNISTD_H
303
0
    if (!mxcfg->omit_pid) {
304
0
        pid_suffix = apr_psprintf(p, ".%" APR_PID_T_FMT, getpid());
305
0
    }
306
0
#endif
307
308
0
    return ap_runtime_dir_relative(p,
309
0
                                   apr_pstrcat(p,
310
0
                                               mxcfg->dir,
311
0
                                               "/",
312
0
                                               type,
313
0
                                               instance_id ? "-" : "",
314
0
                                               instance_id ? instance_id : "",
315
0
                                               pid_suffix,
316
0
                                               NULL));
317
0
}
318
319
static mutex_cfg_t *mxcfg_lookup(apr_pool_t *p, const char *type)
320
0
{
321
0
    mutex_cfg_t *defcfg, *mxcfg, *newcfg;
322
323
0
    defcfg = apr_hash_get(mxcfg_by_type, "default", APR_HASH_KEY_STRING);
324
325
    /* MUST exist in table, or wasn't registered */
326
0
    mxcfg = apr_hash_get(mxcfg_by_type, type, APR_HASH_KEY_STRING);
327
0
    if (!mxcfg) {
328
0
        return NULL;
329
0
    }
330
331
    /* order of precedence:
332
     * 1. Mutex directive for this mutex
333
     * 2. Mutex directive for "default"
334
     * 3. Defaults for this mutex from ap_mutex_register()
335
     * 4. Global defaults
336
     */
337
338
0
    if (mxcfg->set) {
339
0
        newcfg = mxcfg;
340
0
    }
341
0
    else if (defcfg->set) {
342
0
        newcfg = defcfg;
343
0
    }
344
0
    else if (mxcfg->none || mxcfg->mech != APR_LOCK_DEFAULT) {
345
0
        newcfg = mxcfg;
346
0
    }
347
0
    else {
348
0
        newcfg = defcfg;
349
0
    }
350
351
0
    if (!newcfg->none && mutex_needs_file(newcfg->mech) && !newcfg->dir) {
352
        /* a file-based mutex mechanism was configured, but
353
         * without a mutex file directory; go back through
354
         * the chain to find the directory, store in new
355
         * mutex cfg structure
356
         */
357
0
        newcfg = apr_pmemdup(p, newcfg, sizeof *newcfg);
358
359
        /* !true if dir not already set: mxcfg->set && defcfg->dir */
360
0
        if (defcfg->set && defcfg->dir) {
361
0
            newcfg->dir = defcfg->dir;
362
0
        }
363
0
        else if (mxcfg->dir) {
364
0
            newcfg->dir = mxcfg->dir;
365
0
        }
366
0
        else {
367
0
            newcfg->dir = defcfg->dir;
368
0
        }
369
0
    }
370
371
0
    return newcfg;
372
0
}
373
374
static void log_bad_create_options(server_rec *s, const char *type)
375
0
{
376
0
    ap_log_error(APLOG_MARK, APLOG_EMERG, 0, s, APLOGNO(00021)
377
0
                 "Invalid options were specified when creating the %s mutex",
378
0
                 type);
379
0
}
380
381
static void log_unknown_type(server_rec *s, const char *type)
382
0
{
383
0
    ap_log_error(APLOG_MARK, APLOG_EMERG, 0, s, APLOGNO(00022)
384
0
                 "Can't create mutex of unknown type %s", type);
385
0
}
386
387
static void log_create_failure(apr_status_t rv, server_rec *s, const char *type,
388
                               const char *fname)
389
0
{
390
0
    ap_log_error(APLOG_MARK, APLOG_EMERG, rv, s, APLOGNO(00023)
391
0
                 "Couldn't create the %s mutex %s%s%s", type,
392
0
                 fname ? "(file " : "",
393
0
                 fname ? fname : "",
394
0
                 fname ? ")" : "");
395
0
}
396
397
#ifdef AP_NEED_SET_MUTEX_PERMS
398
static void log_perms_failure(apr_status_t rv, server_rec *s, const char *type)
399
0
{
400
0
    ap_log_error(APLOG_MARK, APLOG_EMERG, rv, s, APLOGNO(00024)
401
0
                 "Couldn't set permissions on the %s mutex; "
402
0
                 "check User and Group directives",
403
0
                 type);
404
0
}
405
#endif
406
407
AP_DECLARE(apr_status_t) ap_global_mutex_create(apr_global_mutex_t **mutex,
408
                                                const char **name,
409
                                                const char *type,
410
                                                const char *instance_id,
411
                                                server_rec *s, apr_pool_t *p,
412
                                                apr_int32_t options)
413
0
{
414
0
    apr_status_t rv;
415
0
    const char *fname;
416
0
    mutex_cfg_t *mxcfg = mxcfg_lookup(p, type);
417
418
0
    if (options) {
419
0
        log_bad_create_options(s, type);
420
0
        return APR_EINVAL;
421
0
    }
422
423
0
    if (!mxcfg) {
424
0
        log_unknown_type(s, type);
425
0
        return APR_EINVAL;
426
0
    }
427
428
0
    if (mxcfg->none) {
429
0
        *mutex = NULL;
430
0
        return APR_SUCCESS;
431
0
    }
432
433
0
    fname = get_mutex_filename(p, mxcfg, type, instance_id);
434
435
0
    rv = apr_global_mutex_create(mutex, fname, mxcfg->mech, p);
436
0
    if (rv != APR_SUCCESS) {
437
0
        log_create_failure(rv, s, type, fname);
438
0
        return rv;
439
0
    }
440
441
0
    if (name)
442
0
        *name = fname;
443
444
0
#ifdef AP_NEED_SET_MUTEX_PERMS
445
0
    rv = ap_unixd_set_global_mutex_perms(*mutex);
446
0
    if (rv != APR_SUCCESS) {
447
0
        log_perms_failure(rv, s, type);
448
0
    }
449
0
#endif
450
451
0
    return rv;
452
0
}
453
454
AP_DECLARE(apr_status_t) ap_proc_mutex_create(apr_proc_mutex_t **mutex,
455
                                              const char **name,
456
                                              const char *type,
457
                                              const char *instance_id,
458
                                              server_rec *s, apr_pool_t *p,
459
                                              apr_int32_t options)
460
0
{
461
0
    apr_status_t rv;
462
0
    const char *fname;
463
0
    mutex_cfg_t *mxcfg = mxcfg_lookup(p, type);
464
465
0
    if (options) {
466
0
        log_bad_create_options(s, type);
467
0
        return APR_EINVAL;
468
0
    }
469
470
0
    if (!mxcfg) {
471
0
        log_unknown_type(s, type);
472
0
        return APR_EINVAL;
473
0
    }
474
475
0
    if (mxcfg->none) {
476
0
        *mutex = NULL;
477
0
        return APR_SUCCESS;
478
0
    }
479
480
0
    fname = get_mutex_filename(p, mxcfg, type, instance_id);
481
482
0
    rv = apr_proc_mutex_create(mutex, fname, mxcfg->mech, p);
483
0
    if (rv != APR_SUCCESS) {
484
0
        log_create_failure(rv, s, type, fname);
485
0
        return rv;
486
0
    }
487
488
0
    if (name)
489
0
        *name = fname;
490
491
0
#ifdef AP_NEED_SET_MUTEX_PERMS
492
0
    rv = ap_unixd_set_proc_mutex_perms(*mutex);
493
0
    if (rv != APR_SUCCESS) {
494
0
        log_perms_failure(rv, s, type);
495
0
    }
496
0
#endif
497
498
0
    return rv;
499
0
}
500
501
AP_CORE_DECLARE(void) ap_dump_mutexes(apr_pool_t *p, server_rec *s, apr_file_t *out)
502
0
{
503
0
    apr_hash_index_t *idx;
504
0
    mutex_cfg_t *defcfg = apr_hash_get(mxcfg_by_type, "default", APR_HASH_KEY_STRING);
505
0
    for (idx = apr_hash_first(p, mxcfg_by_type); idx; idx = apr_hash_next(idx))
506
0
    {
507
0
        mutex_cfg_t *mxcfg;
508
0
        const char *name, *mech = "<unknown>";
509
0
        const void *name_;
510
0
        const char *dir = "";
511
0
        apr_hash_this(idx, &name_, NULL, NULL);
512
0
        name = name_;
513
0
        mxcfg = mxcfg_lookup(p, name);
514
0
        if (mxcfg == defcfg && strcmp(name, "default") != 0) {
515
0
            apr_file_printf(out, "Mutex %s: using_defaults\n", name);
516
0
            continue;
517
0
        }
518
0
        if (mxcfg->none) {
519
0
            apr_file_printf(out, "Mutex %s: none\n", name);
520
0
            continue;
521
0
        }
522
0
        switch (mxcfg->mech) {
523
0
        case APR_LOCK_DEFAULT:
524
0
            mech = "default";
525
0
            break;
526
0
#if APR_HAS_FCNTL_SERIALIZE
527
0
        case APR_LOCK_FCNTL:
528
0
            mech = "fcntl";
529
0
            break;
530
0
#endif
531
0
#if APR_HAS_FLOCK_SERIALIZE
532
0
        case APR_LOCK_FLOCK:
533
0
            mech = "flock";
534
0
            break;
535
0
#endif
536
0
#if APR_HAS_POSIXSEM_SERIALIZE
537
0
        case APR_LOCK_POSIXSEM:
538
0
            mech = "posixsem";
539
0
            break;
540
0
#endif
541
0
#if APR_HAS_SYSVSEM_SERIALIZE
542
0
        case APR_LOCK_SYSVSEM:
543
0
            mech = "sysvsem";
544
0
            break;
545
0
#endif
546
0
#if APR_HAS_PROC_PTHREAD_SERIALIZE
547
0
        case APR_LOCK_PROC_PTHREAD:
548
0
            mech = "pthread";
549
0
            break;
550
0
#endif
551
0
        default:
552
0
            ap_assert(0);
553
0
        }
554
555
0
        if (mxcfg->dir)
556
0
            dir = ap_runtime_dir_relative(p, mxcfg->dir);
557
558
0
        apr_file_printf(out, "Mutex %s: dir=\"%s\" mechanism=%s %s\n", name, dir, mech,
559
0
                        mxcfg->omit_pid ? "[OmitPid]" : "");
560
0
    }
561
0
}