libselinux: enable usage with pedantic UB sanitizers

Clang's undefined behavior sanitizer supports checking for unsigned
integer overflow and underflow, and implicit conversions.  While those
operations are well-defined by the C language they can signal logic
mistakes or processing of unchecked user input.

Annotate functions deliberately making use of integer overflow and adopt
the remaining code sites.

Example reports:

    stringrep.c:348:7: runtime error: left shift of 2147483648 by 1 places cannot be represented in type 'access_vector_t' (aka 'unsigned int')
    seusers.c:98:14: runtime error: implicit conversion from type 'int' of value -1 (32-bit, signed) to type 'gid_t' (aka 'unsigned int') changed the value to 4294967295 (32-bit, unsigned)

Signed-off-by: Christian Göttsche <cgzones@googlemail.com>
Acked-by: James Carter <jwcart2@gmail.com>
This commit is contained in:
Christian Göttsche 2023-12-19 17:09:32 +01:00 committed by James Carter
parent a1fa1c8ad7
commit 454a9f248b
12 changed files with 42 additions and 11 deletions

View File

@ -229,13 +229,15 @@ int avc_open(struct selinux_opt *opts, unsigned nopts)
{
avc_setenforce = 0;
while (nopts--)
while (nopts) {
nopts--;
switch(opts[nopts].type) {
case AVC_OPT_SETENFORCE:
avc_setenforce = 1;
avc_enforcing = !!opts[nopts].value;
break;
}
}
return avc_init_internal("avc", NULL, NULL, NULL, NULL);
}

View File

@ -13,6 +13,7 @@
#include "avc_sidtab.h"
#include "avc_internal.h"
ignore_unsigned_overflow_
static inline unsigned sidtab_hash(const char * key)
{
unsigned int hash = 5381;

View File

@ -60,7 +60,8 @@ static inline struct selabel_digest *selabel_is_digest_set
{
struct selabel_digest *digest = NULL;
while (n--) {
while (n) {
n--;
if (opts[n].type == SELABEL_OPT_DIGEST &&
!!opts[n].value) {
digest = calloc(1, sizeof(*digest));
@ -112,9 +113,11 @@ static void selabel_digest_fini(struct selabel_digest *ptr)
static inline int selabel_is_validate_set(const struct selinux_opt *opts,
unsigned n)
{
while (n--)
while (n) {
n--;
if (opts[n].type == SELABEL_OPT_VALIDATE)
return !!opts[n].value;
}
return 0;
}

View File

@ -152,7 +152,8 @@ static int init(struct selabel_handle *rec, const struct selinux_opt *opts,
struct stat sb;
/* Process arguments */
while (n--)
while (n) {
n--;
switch (opts[n].type) {
case SELABEL_OPT_PATH:
path = opts[n].value;
@ -165,6 +166,7 @@ static int init(struct selabel_handle *rec, const struct selinux_opt *opts,
errno = EINVAL;
return -1;
}
}
if (!path)
return -1;

View File

@ -263,7 +263,8 @@ db_init(const struct selinux_opt *opts, unsigned nopts,
* the default one. If RDBMS is not SE-PostgreSQL, it may need to
* specify an explicit specfile for database objects.
*/
while (nopts--) {
while (nopts) {
nopts--;
switch (opts[nopts].type) {
case SELABEL_OPT_PATH:
path = opts[nopts].value;

View File

@ -68,7 +68,7 @@ static int find_stem_from_file(struct saved_data *data, const char *key)
/*
* hash calculation and key comparison of hash table
*/
ignore_unsigned_overflow_
static unsigned int symhash(hashtab_t h, const_hashtab_key_t key)
{
const struct chkdups_key *k = (const struct chkdups_key *)key;
@ -801,7 +801,8 @@ static int init(struct selabel_handle *rec, const struct selinux_opt *opts,
int status = -1, baseonly = 0;
/* Process arguments */
while (n--)
while (n) {
n--;
switch(opts[n].type) {
case SELABEL_OPT_PATH:
path = opts[n].value;
@ -820,6 +821,7 @@ static int init(struct selabel_handle *rec, const struct selinux_opt *opts,
errno = EINVAL;
return -1;
}
}
#if !defined(BUILD_HOST) && !defined(ANDROID)
char subs_file[PATH_MAX + 1];

View File

@ -80,7 +80,8 @@ static int init(struct selabel_handle *rec, const struct selinux_opt *opts,
struct stat sb;
/* Process arguments */
while (n--)
while (n) {
n--;
switch(opts[n].type) {
case SELABEL_OPT_PATH:
path = opts[n].value;
@ -93,6 +94,7 @@ static int init(struct selabel_handle *rec, const struct selinux_opt *opts,
errno = EINVAL;
return -1;
}
}
/* Open the specification file. */
if (!path)

View File

@ -107,7 +107,8 @@ static int init(struct selabel_handle *rec, const struct selinux_opt *opts,
struct stat sb;
/* Process arguments */
while (n--)
while (n) {
n--;
switch(opts[n].type) {
case SELABEL_OPT_PATH:
path = opts[n].value;
@ -120,6 +121,7 @@ static int init(struct selabel_handle *rec, const struct selinux_opt *opts,
errno = EINVAL;
return -1;
}
}
/* Open the specification file. */
if (!path)

View File

@ -102,4 +102,15 @@ size_t strlcpy(char *dest, const char *src, size_t size);
void *reallocarray(void *ptr, size_t nmemb, size_t size);
#endif
/* Use to ignore intentional unsigned under- and overflows while running under UBSAN. */
#if defined(__clang__) && defined(__clang_major__) && (__clang_major__ >= 4)
#if (__clang_major__ >= 12)
#define ignore_unsigned_overflow_ __attribute__((no_sanitize("unsigned-integer-overflow", "unsigned-shift-base")))
#else
#define ignore_unsigned_overflow_ __attribute__((no_sanitize("unsigned-integer-overflow")))
#endif
#else
#define ignore_unsigned_overflow_
#endif
#endif /* SELINUX_INTERNAL_H_ */

View File

@ -99,7 +99,7 @@ int require_seusers = 0;
static gid_t get_default_gid(const char *name) {
struct passwd pwstorage, *pwent = NULL;
gid_t gid = -1;
gid_t gid = (gid_t)-1;
/* Allocate space for the getpwnam_r buffer */
char *rbuf = NULL;
long rbuflen = sysconf(_SC_GETPW_R_SIZE_MAX);

View File

@ -26,6 +26,8 @@
#include "sha1.h"
#include <memory.h>
#include "selinux_internal.h"
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// TYPES
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
@ -62,6 +64,7 @@ typedef union
//
// Hash a single 512-bit block. This is the core of the algorithm
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
ignore_unsigned_overflow_
static
void
TransformFunction

View File

@ -337,13 +337,15 @@ void print_access_vector(security_class_t tclass, access_vector_t av)
printf(" {");
while (av) {
for (;;) {
if (av & bit) {
permstr = security_av_perm_to_string(tclass, bit);
if (!permstr)
break;
printf(" %s", permstr);
av &= ~bit;
if (!av)
break;
}
bit <<= 1;
}