Reputation: 505
I am learning C and in one of the tasks I am asked to use 1000 * getuid() + 0
to generate the semaphore name for sem_open
. There will be multiple semaphores and the last digit (+ 0
) is used to differentiate them.
Code snippet:
#define SEM_NAME_LENGTH 24
#define SEM_NAME 1000 * getuid() + 0
...
char sem_name[SEM_NAME_LENGTH];
/* convert the sem number (as defined in spec) to a string */
if (sprintf(sem_name, "%u", SEM_NAME) < 0) {
return -1;
}
sem_id = sem_open(sem_name, O_CREAT, S_IRUSR | S_IWUSR, 0);
It is evident that SEM_NAME
can exceed unsigned int
, because getuid
can return UINT_MAX
, and if we multiply that by 1000...
My first thought was to change the sprintf
format to "%llu"
, but then I get a warning:
format specifies type 'unsigned long long' but the argument has type 'unsigned int'
Which means that compilers are still considering the expression to be an unsigned int
or __uid_t
.
Looks like I have the following possibilities:
A. Manually typecast to unsigned long long
:
#define SEM_NAME (unsigned long long) 1000 * getuid() + 0
B. Define the name as variable:
unsigned long long sem_name = 1000 * getuid() + 0;
C. Check for overflows / not accept uids higher than UINT_MAX
/1000 (bad)
I am also quite surprised that compilers (gcc
, clang
) don't detect the problem themselves. I am using -std=gnu99 -Wall -Wextra -Wstrict-prototypes -pedantic
.
Upvotes: 0
Views: 108
Reputation: 153517
C Check for overflows
As needed, pedantic code checks for potential range issues.
uid_t id = getuid();
if (id > UINT_MAX/1000 || id < 0) Handle_OutOfRange(id);
sprintf(sem_name, "%u", (unsigned) (id * 1000u));
More practically...
Interestingly code use "%u"
, even though the type was not know to be unsigned
as getuid()
returns type uid_t
which can be signed
or unsigned
and of a width different than unsigned
.
// potential undefined behavior - UB
sprintf(sem_name, "%u", SEM_NAME)
Following OP's A approach of promoting to a wide type, code could use the following of going to the widest type. IMO, why go to long long/unsigned long long
? Go the the widest integer type intmax_t/uintmax_t
.
uid_t id = getuid();
sprintf(sem_name, "%jd", (intmax_t) id * 1000);
In general, do not recommend OP's #define
which hides a function call in a macro SEM_NAME
that appears to be a constant. Alternative suggested
// #define SEM_NAME 1000 * getuid() + 0
// sprintf(sem_name, "%u", SEM_NAME)
#include <stdint.h>
#define SEM_NAME(id) (INTMAX_C(1000) * (id) + 0)
uid_t id = getuid();
sprintf(sem_name, "%jd", SEM_NAME(id));
Upvotes: 1