From: Jonathan Gruber <jonathan.gruber.jg@gmail.com>
To: util-linux@vger.kernel.org
Cc: Jonathan Gruber <jonathan.gruber.jg@gmail.com>
Subject: [PATCH] Various subsystems: avoid UB from ref counts
Date: Tue, 24 Feb 2026 02:23:29 -0800 [thread overview]
Message-ID: <20260224102329.69255-1-jonathan.gruber.jg@gmail.com> (raw)
util-linux uses type signed int for reference counters. The functions
that increment reference counters thus risk undefined behavior (UB) if
the value of the reference counter is INT_MAX prior to the increment,
because overflow in signed integers is UB. A program that creates more
than INT_MAX coexistent references to some reference-counted object
would thus trigger UB by overflowing the reference counter over INT_MAX.
On systems with size_t larger than int, such as many 64-bit systems, it
is possible, given sufficient RAM, for a program to create more than
INT_MAX pointers holding the address of the same reference-counted
object and to attempt to make each of those pointers be a reference to
the reference-counted object, thereby overflowing the reference counter
over INT_MAX and triggering UB. It is best to protect against the
demonstrated *possibility* of this UB-triggering scenario regardless of
the scenario's probability.
While signed integer overflow is UB, it is likely to manifest in this
scenario as the reference counter wrapping from INT_MAX to INT_MIN.
However, the functions that decrement the reference counters deallocate
the reference-counted object if the value of the reference counter
becomes less than or equal to zero after the decrement. Therefore, if
the reference counter starts with a value of one and is incremented
INT_MAX + 2 times with no intervening decrements, then, assuming that
the resulting integer overflow manifests as a wrap from INT_MAX to
INT_MIN, the value of the reference counter would become INT_MIN + 2. If
the reference counter is then decremented once, then its value would
become INT_MIN + 1, which is less than zero, so the reference-counted
object would be deallocated even though there are still
1 + (INT_MAX + 2) - 1 = INT_MAX + 2 live references to the object. Since
the object has been deallocated, any access to the remaining references
would trigger UB. Additionally, if the reference counter is *attempted*
to be decremented again (which would trigger UB since the
reference-counted object has been deallocated), then, assuming that the
memory formerly occupied by the reference-counted object has somehow not
been reallocated to another object or overwritten, and assuming that the
UB from the attempted decrement manifests as an actual decrement, the
"value" of the (deallocated) reference counter would become INT_MIN,
which is less than zero, so the already-deallocated reference-counted
object would be deallocated again, resulting in a double free, which
also triggers UB.
This patch attempts to address the UB from the reference counters. In
order to avoid UB from overflowing the reference counters, this patch
changes the types of the reference counters to an unsigned integer type,
since overflow for unsigned integer types is not UB but wraps; in this
case, if the reference counter overflows, then its value would wrap to
zero. In order to avoid the UB resulting from deallocating the
reference-counted object when there are still live references to the
object, this patch ensures that the value of the reference counter is
zero only if there are actually no live references to the
reference-counted object. This is done by calling abort() in the
functions that increment reference counters when the reference counter
overflows; this behavior is also documented in the documentation
comments for the functions. The choice to call abort() was made for
simplicity and because the *least bad* result of accessing a reference
counted object that has been deallocated too early is for the program to
crash, which is also what calling abort() triggers. Finally, the
unsigned integer type that was chosen for the reference counters is
size_t; this choice minimizes the risk of overflowing the reference
counter and triggering an abort() because storing more than SIZE_MAX
coexistent pointers/references to a reference-counted object would
require more than SIZE_MAX bytes of memory. size_t can be larger than
int, so this patch might increase the sizes of some reference-counted
objects, but this increase should be miniscule.
Signed-off-by: Jonathan Gruber <jonathan.gruber.jg@gmail.com>
---
include/path.h | 2 +-
lib/path.c | 22 +++++++++++++++--
libfdisk/src/ask.c | 18 +++++++++++---
libfdisk/src/context.c | 18 ++++++++++++--
libfdisk/src/fdiskP.h | 15 ++++++------
libfdisk/src/item.c | 16 +++++++++++--
libfdisk/src/partition.c | 19 ++++++++++++---
libfdisk/src/parttype.c | 16 +++++++++++--
libfdisk/src/script.c | 19 ++++++++++++---
libfdisk/src/table.c | 17 +++++++++++--
libmount/src/cache.c | 20 ++++++++++++----
libmount/src/fs.c | 21 ++++++++++++----
libmount/src/fs_statmount.c | 18 +++++++++++---
libmount/src/lock.c | 21 ++++++++++++----
libmount/src/monitor.c | 16 +++++++++++--
libmount/src/monitor.h | 3 ++-
libmount/src/mountP.h | 10 ++++----
libmount/src/optlist.c | 23 ++++++++++++++++--
libmount/src/tab.c | 19 +++++++++++----
libsmartcols/src/column.c | 15 ++++++++++--
libsmartcols/src/filter.c | 34 +++++++++++++++++++++++---
libsmartcols/src/grouping.c | 24 +++++++++++++++++--
libsmartcols/src/line.c | 15 ++++++++++--
libsmartcols/src/smartcolsP.h | 18 +++++++-------
libsmartcols/src/symbols.c | 15 ++++++++++--
libsmartcols/src/table.c | 15 ++++++++++--
misc-utils/lsblk-devtree.c | 45 +++++++++++++++++++++++++++++++----
misc-utils/lsblk.h | 4 ++--
sys-utils/lscpu-cpu.c | 23 ++++++++++++++++--
sys-utils/lscpu-cputype.c | 23 +++++++++++++++---
sys-utils/lscpu.h | 5 ++--
31 files changed, 454 insertions(+), 95 deletions(-)
diff --git a/include/path.h b/include/path.h
index 60a6162df..1b403d66e 100644
--- a/include/path.h
+++ b/include/path.h
@@ -17,7 +17,7 @@ struct path_cxt {
int dir_fd;
char *dir_path;
- int refcount;
+ size_t refcount;
char *prefix;
char path_buffer[PATH_MAX];
diff --git a/lib/path.c b/lib/path.c
index 6ce8a10d2..10aaa7b5c 100644
--- a/lib/path.c
+++ b/lib/path.c
@@ -18,6 +18,7 @@
#include <string.h>
#include <unistd.h>
#include <stdio.h>
+#include <stdlib.h>
#include <inttypes.h>
#include <errno.h>
@@ -79,10 +80,27 @@ fail:
return NULL;
}
+/**
+ * ul_ref_path:
+ * @pc: path_cxt instance
+ *
+ * Increments reference counter.
+ * Calls abort() if reference counter equals SIZE_MAX prior to the increment.
+ */
void ul_ref_path(struct path_cxt *pc)
{
- if (pc)
+ if (pc) {
pc->refcount++;
+ /*
+ * pc->refcount == 0 now if and only if
+ * pc->refcount == SIZE_MAX prior to the increment.
+ *
+ * Checking for overflow after the increment instead of before
+ * seems to generate better assembly.
+ */
+ if (pc->refcount == 0)
+ abort();
+ }
}
void ul_unref_path(struct path_cxt *pc)
@@ -92,7 +110,7 @@ void ul_unref_path(struct path_cxt *pc)
pc->refcount--;
- if (pc->refcount <= 0) {
+ if (pc->refcount == 0) {
DBG(CXT, ul_debugobj(pc, "dealloc"));
if (pc->dialect)
pc->free_dialect(pc);
diff --git a/libfdisk/src/ask.c b/libfdisk/src/ask.c
index 507cc6fc6..3ce472aa4 100644
--- a/libfdisk/src/ask.c
+++ b/libfdisk/src/ask.c
@@ -3,6 +3,7 @@
#include "fdiskP.h"
#include <stdarg.h>
+#include <stdlib.h>
/**
* SECTION: ask
@@ -49,7 +50,7 @@ struct fdisk_ask *fdisk_new_ask(void)
void fdisk_reset_ask(struct fdisk_ask *ask)
{
- int refcount;
+ size_t refcount;
assert(ask);
free(ask->query);
@@ -69,11 +70,22 @@ void fdisk_reset_ask(struct fdisk_ask *ask)
* @ask: ask instance
*
* Increments reference counter.
+ * Calls abort() if reference counter equals SIZE_MAX prior to the increment.
*/
void fdisk_ref_ask(struct fdisk_ask *ask)
{
- if (ask)
+ if (ask) {
ask->refcount++;
+ /*
+ * ask->refcount == 0 now if and only if
+ * ask->refcount == SIZE_MAX prior to the increment.
+ *
+ * Checking for overflow after the increment instead of before
+ * seems to generate better assembly.
+ */
+ if (ask->refcount == 0)
+ abort();
+ }
}
@@ -90,7 +102,7 @@ void fdisk_unref_ask(struct fdisk_ask *ask)
return;
ask->refcount--;
- if (ask->refcount <= 0) {
+ if (ask->refcount == 0) {
fdisk_reset_ask(ask);
DBG(ASK, ul_debugobj(ask, "free"));
free(ask);
diff --git a/libfdisk/src/context.c b/libfdisk/src/context.c
index bc46f1f62..6d8faa8ff 100644
--- a/libfdisk/src/context.c
+++ b/libfdisk/src/context.c
@@ -1,3 +1,6 @@
+
+#include <stdlib.h>
+
#ifdef HAVE_LIBBLKID
# include <blkid.h>
#endif
@@ -199,11 +202,22 @@ struct fdisk_context *fdisk_new_nested_context(struct fdisk_context *parent,
* @cxt: context pointer
*
* Increments reference counter.
+ * Calls abort() if reference counter equals SIZE_MAX prior to the increment.
*/
void fdisk_ref_context(struct fdisk_context *cxt)
{
- if (cxt)
+ if (cxt) {
cxt->refcount++;
+ /*
+ * ctx->refcount == 0 now if and only if
+ * ctx->refcount == SIZE_MAX prior to the increment.
+ *
+ * Checking for overflow after the increment instead of before
+ * seems to generate better assembly.
+ */
+ if (cxt->refcount == 0)
+ abort();
+ }
}
/**
@@ -1089,7 +1103,7 @@ void fdisk_unref_context(struct fdisk_context *cxt)
return;
cxt->refcount--;
- if (cxt->refcount <= 0) {
+ if (cxt->refcount == 0) {
DBG(CXT, ul_debugobj(cxt, "freeing context %p for %s", cxt, cxt->dev_path));
reset_context(cxt); /* this is sensitive to parent<->child relationship! */
diff --git a/libfdisk/src/fdiskP.h b/libfdisk/src/fdiskP.h
index e3e57929a..e1af40588 100644
--- a/libfdisk/src/fdiskP.h
+++ b/libfdisk/src/fdiskP.h
@@ -107,12 +107,13 @@ struct fdisk_iter {
* Partition types
*/
struct fdisk_parttype {
- unsigned int code; /* type as number or zero */
+ size_t refcount; /* reference counter for allocated types */
+
char *name; /* description */
char *typestr; /* type as string or NULL */
+ unsigned int code; /* type as number or zero */
unsigned int flags; /* FDISK_PARTTYPE_* flags */
- int refcount; /* reference counter for allocated types */
};
enum {
@@ -136,7 +137,7 @@ struct fdisk_shortcut {
};
struct fdisk_partition {
- int refcount; /* reference counter */
+ size_t refcount; /* reference counter */
size_t partno; /* partition number */
size_t parent_partno; /* for logical partitions */
@@ -200,7 +201,7 @@ enum {
struct fdisk_table {
struct list_head parts; /* partitions */
- int refcount;
+ size_t refcount;
size_t nents; /* number of partitions */
};
@@ -340,7 +341,7 @@ struct fdisk_ask {
int type; /* FDISK_ASKTYPE_* */
char *query;
- int refcount;
+ size_t refcount;
union {
/* FDISK_ASKTYPE_{NUMBER,OFFSET} */
@@ -384,7 +385,7 @@ struct fdisk_context {
char *dev_model; /* on linux /sys/block/<name>/device/model or NULL */
struct stat dev_st; /* stat(2) result */
- int refcount;
+ size_t refcount;
unsigned char *firstsector; /* buffer with master boot record */
unsigned long firstsector_bufsz;
@@ -484,7 +485,7 @@ extern int fdisk_probe_labels(struct fdisk_context *cxt);
extern void fdisk_deinit_label(struct fdisk_label *lb);
struct fdisk_labelitem {
- int refcount; /* reference counter */
+ size_t refcount; /* reference counter */
int id; /* <label>_ITEM_* */
char type; /* s = string, j = uint64 */
const char *name; /* human readable name */
diff --git a/libfdisk/src/item.c b/libfdisk/src/item.c
index 9e2f44ca1..3c2802c5e 100644
--- a/libfdisk/src/item.c
+++ b/libfdisk/src/item.c
@@ -1,5 +1,6 @@
#include <inttypes.h>
+#include <stdlib.h>
#include "fdiskP.h"
@@ -53,6 +54,8 @@ struct fdisk_labelitem *fdisk_new_labelitem(void)
* @li: label item
*
* Increments reference counter.
+ * Calls abort() if reference counter equals SIZE_MAX prior to the increment.
+ *
* Since: 2.29
*/
void fdisk_ref_labelitem(struct fdisk_labelitem *li)
@@ -61,6 +64,15 @@ void fdisk_ref_labelitem(struct fdisk_labelitem *li)
/* make sure we do not use refcounting for static items */
assert(li->refcount > 0);
li->refcount++;
+ /*
+ * li->refcount == 0 now if and only if
+ * li->refcount == SIZE_MAX prior to the increment.
+ *
+ * Checking for overflow after the increment instead of before
+ * seems to generate better assembly.
+ */
+ if (li->refcount == 0)
+ abort();
}
}
@@ -75,7 +87,7 @@ void fdisk_ref_labelitem(struct fdisk_labelitem *li)
*/
int fdisk_reset_labelitem(struct fdisk_labelitem *li)
{
- int refcount;
+ size_t refcount;
if (!li)
return -EINVAL;
@@ -106,7 +118,7 @@ void fdisk_unref_labelitem(struct fdisk_labelitem *li)
assert(li->refcount > 0);
li->refcount--;
- if (li->refcount <= 0) {
+ if (li->refcount == 0) {
DBG(ITEM, ul_debugobj(li, "free"));
fdisk_reset_labelitem(li);
free(li);
diff --git a/libfdisk/src/partition.c b/libfdisk/src/partition.c
index 9a5614d21..0ef16b602 100644
--- a/libfdisk/src/partition.c
+++ b/libfdisk/src/partition.c
@@ -1,4 +1,6 @@
+#include <stdlib.h>
+
#include "c.h"
#include "strutils.h"
@@ -57,7 +59,7 @@ struct fdisk_partition *fdisk_new_partition(void)
*/
void fdisk_reset_partition(struct fdisk_partition *pa)
{
- int ref;
+ size_t ref;
if (!pa)
return;
@@ -137,11 +139,22 @@ static struct fdisk_partition *__copy_partition(struct fdisk_partition *o)
* @pa: partition pointer
*
* Increments reference counter.
+ * Calls abort() if reference counter equals SIZE_MAX prior to the increment.
*/
void fdisk_ref_partition(struct fdisk_partition *pa)
{
- if (pa)
+ if (pa) {
pa->refcount++;
+ /*
+ * pa->refcount == 0 now if and only if
+ * pa->refcount == SIZE_MAX prior to the increment.
+ *
+ * Checking for overflow after the increment instead of before
+ * seems to generate better assembly.
+ */
+ if (pa->refcount == 0)
+ abort();
+ }
}
/**
@@ -157,7 +170,7 @@ void fdisk_unref_partition(struct fdisk_partition *pa)
return;
pa->refcount--;
- if (pa->refcount <= 0) {
+ if (pa->refcount == 0) {
list_del(&pa->parts);
fdisk_reset_partition(pa);
DBG(PART, ul_debugobj(pa, "free"));
diff --git a/libfdisk/src/parttype.c b/libfdisk/src/parttype.c
index 8e36ada74..026d8ef64 100644
--- a/libfdisk/src/parttype.c
+++ b/libfdisk/src/parttype.c
@@ -1,5 +1,6 @@
#include <ctype.h>
+#include <stdlib.h>
#include "fdiskP.h"
#include "cctype.h"
@@ -41,11 +42,22 @@ struct fdisk_parttype *fdisk_new_parttype(void)
* @t: partition type
*
* Increments reference counter for allocated types
+ * Calls abort() if reference counter equals SIZE_MAX prior to the increment.
*/
void fdisk_ref_parttype(struct fdisk_parttype *t)
{
- if (fdisk_parttype_is_allocated(t))
+ if (fdisk_parttype_is_allocated(t)) {
t->refcount++;
+ /*
+ * t->refcount == 0 now if and only if
+ * t->refcount == SIZE_MAX prior to the increment.
+ *
+ * Checking for overflow after the increment instead of before
+ * seems to generate better assembly.
+ */
+ if (t->refcount == 0)
+ abort();
+ }
}
/**
@@ -61,7 +73,7 @@ void fdisk_unref_parttype(struct fdisk_parttype *t)
return;
t->refcount--;
- if (t->refcount <= 0) {
+ if (t->refcount == 0) {
DBG(PARTTYPE, ul_debugobj(t, "free"));
free(t->typestr);
free(t->name);
diff --git a/libfdisk/src/script.c b/libfdisk/src/script.c
index 62ca5eeb2..6ad1c9261 100644
--- a/libfdisk/src/script.c
+++ b/libfdisk/src/script.c
@@ -1,4 +1,6 @@
+#include <stdlib.h>
+
#include "cctype.h"
#include "fdiskP.h"
#include "strutils.h"
@@ -56,7 +58,7 @@ struct fdisk_script {
struct list_head headers;
struct fdisk_context *cxt;
- int refcount;
+ size_t refcount;
char *(*fn_fgets)(struct fdisk_script *, char *, size_t, FILE *);
void *userdata;
@@ -158,11 +160,22 @@ done:
* @dp: script pointer
*
* Increments reference counter.
+ * Calls abort() if reference counter equals SIZE_MAX prior to the increment.
*/
void fdisk_ref_script(struct fdisk_script *dp)
{
- if (dp)
+ if (dp) {
dp->refcount++;
+ /*
+ * dp->refcount == 0 now if and only if
+ * dp->refcount == SIZE_MAX prior to the increment.
+ *
+ * Checking for overflow after the increment instead of before
+ * seems to generate better assembly.
+ */
+ if (dp->refcount == 0)
+ abort();
+ }
}
static void fdisk_reset_script(struct fdisk_script *dp)
@@ -195,7 +208,7 @@ void fdisk_unref_script(struct fdisk_script *dp)
return;
dp->refcount--;
- if (dp->refcount <= 0) {
+ if (dp->refcount == 0) {
fdisk_reset_script(dp);
fdisk_unref_context(dp->cxt);
fdisk_unref_table(dp->table);
diff --git a/libfdisk/src/table.c b/libfdisk/src/table.c
index 30955e1a6..ea9f26f7e 100644
--- a/libfdisk/src/table.c
+++ b/libfdisk/src/table.c
@@ -1,4 +1,6 @@
+#include <stdlib.h>
+
#include "fdiskP.h"
/**
@@ -66,11 +68,22 @@ int fdisk_reset_table(struct fdisk_table *tb)
* @tb: table pointer
*
* Increments reference counter.
+ * Calls abort() if reference counter equals SIZE_MAX prior to the increment.
*/
void fdisk_ref_table(struct fdisk_table *tb)
{
- if (tb)
+ if (tb) {
tb->refcount++;
+ /*
+ * tb->refcount == 0 now if and only if
+ * tb->refcount == SIZE_MAX prior to the increment.
+ *
+ * Checking for overflow after the increment instead of before
+ * seems to generate better assembly.
+ */
+ if (tb->refcount == 0)
+ abort();
+ }
}
/**
@@ -86,7 +99,7 @@ void fdisk_unref_table(struct fdisk_table *tb)
return;
tb->refcount--;
- if (tb->refcount <= 0) {
+ if (tb->refcount == 0) {
fdisk_reset_table(tb);
DBG(TAB, ul_debugobj(tb, "free"));
diff --git a/libmount/src/cache.c b/libmount/src/cache.c
index 4d2a20f23..e94c67cf7 100644
--- a/libmount/src/cache.c
+++ b/libmount/src/cache.c
@@ -61,7 +61,7 @@ struct libmnt_cache {
struct mnt_cache_entry *ents;
size_t nents;
size_t nallocs;
- int refcount;
+ size_t refcount;
int probe_sb_extra; /* extra BLKID_SUBLKS_* flags */
/* blkid_evaluate_tag() works in two ways:
@@ -126,7 +126,7 @@ void mnt_free_cache(struct libmnt_cache *cache)
if (!cache)
return;
- DBG(CACHE, ul_debugobj(cache, "free [refcount=%d]", cache->refcount));
+ DBG(CACHE, ul_debugobj(cache, "free [refcount=%zu]", cache->refcount));
for (i = 0; i < cache->nents; i++) {
struct mnt_cache_entry *e = &cache->ents[i];
@@ -145,12 +145,22 @@ void mnt_free_cache(struct libmnt_cache *cache)
* @cache: cache pointer
*
* Increments reference counter.
+ * Calls abort() if reference counter equals SIZE_MAX prior to the increment.
*/
void mnt_ref_cache(struct libmnt_cache *cache)
{
if (cache) {
cache->refcount++;
- /*DBG(CACHE, ul_debugobj(cache, "ref=%d", cache->refcount));*/
+ /*DBG(CACHE, ul_debugobj(cache, "ref=%zu", cache->refcount));*/
+ /*
+ * cache->refcount == 0 now if and only if
+ * cache->refcount == SIZE_MAX prior to the increment.
+ *
+ * Checking for overflow after the increment instead of before
+ * seems to generate better assembly.
+ */
+ if (cache->refcount == 0)
+ abort();
}
}
@@ -165,8 +175,8 @@ void mnt_unref_cache(struct libmnt_cache *cache)
{
if (cache) {
cache->refcount--;
- /*DBG(CACHE, ul_debugobj(cache, "unref=%d", cache->refcount));*/
- if (cache->refcount <= 0) {
+ /*DBG(CACHE, ul_debugobj(cache, "unref=%zu", cache->refcount));*/
+ if (cache->refcount == 0) {
mnt_unref_table(cache->mountinfo);
mnt_free_cache(cache);
diff --git a/libmount/src/fs.c b/libmount/src/fs.c
index a9b738022..5af562aa8 100644
--- a/libmount/src/fs.c
+++ b/libmount/src/fs.c
@@ -19,6 +19,7 @@
#include <ctype.h>
#include <blkid.h>
#include <stddef.h>
+#include <stdlib.h>
#include "mountP.h"
#include "strutils.h"
@@ -58,7 +59,7 @@ void mnt_free_fs(struct libmnt_fs *fs)
if (!fs)
return;
- DBG(FS, ul_debugobj(fs, "free [refcount=%d]", fs->refcount));
+ DBG(FS, ul_debugobj(fs, "free [refcount=%zu]", fs->refcount));
mnt_reset_fs(fs);
free(fs);
@@ -72,7 +73,7 @@ void mnt_free_fs(struct libmnt_fs *fs)
*/
void mnt_reset_fs(struct libmnt_fs *fs)
{
- int ref;
+ size_t ref;
if (!fs)
return;
@@ -116,12 +117,22 @@ void mnt_reset_fs(struct libmnt_fs *fs)
* @fs: fs pointer
*
* Increments reference counter.
+ * Calls abort() if reference counter equals SIZE_MAX prior to the increment.
*/
void mnt_ref_fs(struct libmnt_fs *fs)
{
if (fs) {
fs->refcount++;
- /*DBG(FS, ul_debugobj(fs, "ref=%d", fs->refcount));*/
+ /*DBG(FS, ul_debugobj(fs, "ref=%zu", fs->refcount));*/
+ /*
+ * fs->refcount == 0 now if and only if
+ * fs->refcount == SIZE_MAX prior to the increment.
+ *
+ * Checking for overflow after the increment instead of before
+ * seems to generate better assembly.
+ */
+ if (fs->refcount == 0)
+ abort();
}
}
@@ -136,8 +147,8 @@ void mnt_unref_fs(struct libmnt_fs *fs)
{
if (fs) {
fs->refcount--;
- /*DBG(FS, ul_debugobj(fs, "unref=%d", fs->refcount));*/
- if (fs->refcount <= 0)
+ /*DBG(FS, ul_debugobj(fs, "unref=%zu", fs->refcount));*/
+ if (fs->refcount == 0)
mnt_free_fs(fs);
}
}
diff --git a/libmount/src/fs_statmount.c b/libmount/src/fs_statmount.c
index 599ee8c52..a1b1dca53 100644
--- a/libmount/src/fs_statmount.c
+++ b/libmount/src/fs_statmount.c
@@ -15,6 +15,8 @@
* @title: statmount setting
* @short_description: Fetches information about mount node from the kernel.
*/
+#include <stdlib.h>
+
#include "mountP.h"
#include "mangle.h"
@@ -56,12 +58,22 @@ struct libmnt_statmnt *mnt_new_statmnt(void)
* @sm: statmount setting
*
* Increments reference counter.
+ * Calls abort() if reference counter equals SIZE_MAX prior to the increment.
*/
void mnt_ref_statmnt(struct libmnt_statmnt *sm)
{
if (sm) {
sm->refcount++;
- /*DBG(STATMNT, ul_debugobj(sm, "ref=%d", sm->refcount));*/
+ /*DBG(STATMNT, ul_debugobj(sm, "ref=%zu", sm->refcount));*/
+ /*
+ * sm->refcount == 0 now if and only if
+ * sm->refcount == SIZE_MAX prior to the increment.
+ *
+ * Checking for overflow after the increment instead of before
+ * seems to generate better assembly.
+ */
+ if (sm->refcount == 0)
+ abort();
}
}
@@ -76,8 +88,8 @@ void mnt_unref_statmnt(struct libmnt_statmnt *sm)
{
if (sm) {
sm->refcount--;
- /*DBG(STATMNT, ul_debugobj(sm, "unref=%d", sm->refcount));*/
- if (sm->refcount <= 0) {
+ /*DBG(STATMNT, ul_debugobj(sm, "unref=%zu", sm->refcount));*/
+ if (sm->refcount == 0) {
free(sm->buf);
free(sm);
}
diff --git a/libmount/src/lock.c b/libmount/src/lock.c
index 28ff8b788..b4ec0e3f2 100644
--- a/libmount/src/lock.c
+++ b/libmount/src/lock.c
@@ -25,6 +25,7 @@
#include <fcntl.h>
#include <limits.h>
#include <sys/file.h>
+#include <stdlib.h>
#include "strutils.h"
#include "closestream.h"
@@ -36,7 +37,7 @@
* lock handler
*/
struct libmnt_lock {
- int refcount; /* reference counter */
+ size_t refcount; /* reference counter */
char *lockfile; /* path to lock file (e.g. /etc/mtab~) */
int lockfile_fd; /* lock file descriptor */
@@ -101,7 +102,7 @@ void mnt_free_lock(struct libmnt_lock *ml)
if (!ml)
return;
- DBG(LOCKS, ul_debugobj(ml, "free%s [refcount=%d]",
+ DBG(LOCKS, ul_debugobj(ml, "free%s [refcount=%zu]",
ml->locked ? " !!! LOCKED !!!" : "",
ml->refcount));
free(ml->lockfile);
@@ -113,6 +114,7 @@ void mnt_free_lock(struct libmnt_lock *ml)
* @ml: lock pointer
*
* Increments reference counter.
+ * Calls abort() if reference counter equals SIZE_MAX prior to the increment.
*
* Since: 2.40
*/
@@ -120,7 +122,16 @@ void mnt_ref_lock(struct libmnt_lock *ml)
{
if (ml) {
ml->refcount++;
- /*DBG(FS, ul_debugobj(fs, "ref=%d", ml->refcount));*/
+ /*DBG(FS, ul_debugobj(fs, "ref=%zu", ml->refcount));*/
+ /*
+ * ml->refcount == 0 now if and only if
+ * ml->refcount == SIZE_MAX prior to the increment.
+ *
+ * Checking for overflow after the increment instead of before
+ * seems to generate better assembly.
+ */
+ if (ml->refcount == 0)
+ abort();
}
}
@@ -135,8 +146,8 @@ void mnt_unref_lock(struct libmnt_lock *ml)
{
if (ml) {
ml->refcount--;
- /*DBG(FS, ul_debugobj(fs, "unref=%d", ml->refcount));*/
- if (ml->refcount <= 0)
+ /*DBG(FS, ul_debugobj(fs, "unref=%zu", ml->refcount));*/
+ if (ml->refcount == 0)
mnt_free_lock(ml);
}
}
diff --git a/libmount/src/monitor.c b/libmount/src/monitor.c
index 194817aac..35c70857b 100644
--- a/libmount/src/monitor.c
+++ b/libmount/src/monitor.c
@@ -38,6 +38,7 @@
#include "mountP.h"
#include "monitor.h"
+#include <stdlib.h>
#include <sys/epoll.h>
/**
@@ -67,11 +68,22 @@ struct libmnt_monitor *mnt_new_monitor(void)
* @mn: monitor pointer
*
* Increments reference counter.
+ * Calls abort() if reference counter equals SIZE_MAX prior to the increment.
*/
void mnt_ref_monitor(struct libmnt_monitor *mn)
{
- if (mn)
+ if (mn) {
mn->refcount++;
+ /*
+ * mn->refcount == 0 now if and only if
+ * mn->refcount == SIZE_MAX prior to the increment.
+ *
+ * Checking for overflow after the increment instead of before
+ * seems to generate better assembly.
+ */
+ if (mn->refcount == 0)
+ abort();
+ }
}
void free_monitor_entry(struct monitor_entry *me)
@@ -102,7 +114,7 @@ void mnt_unref_monitor(struct libmnt_monitor *mn)
return;
mn->refcount--;
- if (mn->refcount <= 0) {
+ if (mn->refcount == 0) {
mnt_monitor_close_fd(mn); /* destroys all file descriptors */
while (!list_empty(&mn->ents)) {
diff --git a/libmount/src/monitor.h b/libmount/src/monitor.h
index a7859c075..b17b271be 100644
--- a/libmount/src/monitor.h
+++ b/libmount/src/monitor.h
@@ -6,6 +6,7 @@
#include "c.h"
#include <stdbool.h>
+#include <stddef.h>
struct monitor_opers;
@@ -26,7 +27,7 @@ struct monitor_entry {
};
struct libmnt_monitor {
- int refcount;
+ size_t refcount;
int fd; /* public monitor file descriptor */
struct list_head ents;
diff --git a/libmount/src/mountP.h b/libmount/src/mountP.h
index 7a6a4fbe2..41dee526f 100644
--- a/libmount/src/mountP.h
+++ b/libmount/src/mountP.h
@@ -205,13 +205,13 @@ struct libmnt_iter {
* statmount setting; shared between tables and filesystems
*/
struct libmnt_statmnt {
- int refcount;
- uint64_t mask; /* default statmount() mask */
+ size_t refcount;
+ uint64_t mask; /* default statmount() mask */
struct ul_statmount *buf;
size_t bufsiz;
- unsigned int disabled: 1; /* enable or disable statmount() */
+ unsigned int disabled: 1; /* enable or disable statmount() */
};
@@ -223,7 +223,7 @@ struct libmnt_fs {
struct list_head ents;
struct libmnt_table *tab;
- int refcount; /* reference counter */
+ size_t refcount; /* reference counter */
unsigned int opts_age; /* to sync with optlist */
struct libmnt_optlist *optlist;
@@ -322,7 +322,7 @@ static inline void mnt_fs_mark_moved(struct libmnt_fs *fs)
struct libmnt_table {
int fmt; /* MNT_FMT_* file format */
int nents; /* number of entries */
- int refcount; /* reference counter */
+ size_t refcount; /* reference counter */
int comms; /* enable/disable comment parsing */
char *comm_intro; /* First comment in file */
char *comm_tail; /* Last comment in file */
diff --git a/libmount/src/optlist.c b/libmount/src/optlist.c
index 5bb32c6a8..0b6f6cd19 100644
--- a/libmount/src/optlist.c
+++ b/libmount/src/optlist.c
@@ -13,6 +13,8 @@
* The "optlist" is container for parsed mount options.
*
*/
+#include <stdlib.h>
+
#include "strutils.h"
#include "list.h"
#include "mountP.h"
@@ -51,7 +53,7 @@ struct libmnt_opt {
};
struct libmnt_optlist {
- int refcount;
+ size_t refcount;
unsigned int age; /* incremented after each change */
const struct libmnt_optmap *linux_map; /* map with MS_ flags */
@@ -90,10 +92,27 @@ struct libmnt_optlist *mnt_new_optlist(void)
return ls;
}
+/**
+ * mnt_ref_optlist:
+ * @ls: optlist instance
+ *
+ * Increments reference counter.
+ * Calls abort() if reference counter equals SIZE_MAX prior to the increment.
+ */
void mnt_ref_optlist(struct libmnt_optlist *ls)
{
- if (ls)
+ if (ls) {
ls->refcount++;
+ /*
+ * ls->refcount == 0 now if and only if
+ * ls->refcount == SIZE_MAX prior to the increment.
+ *
+ * Checking for overflow after the increment instead of before
+ * seems to generate better assembly.
+ */
+ if (ls->refcount == 0)
+ abort();
+ }
}
static void reset_cache(struct optlist_cache *cache)
diff --git a/libmount/src/tab.c b/libmount/src/tab.c
index 0d2b0ecd4..903cad522 100644
--- a/libmount/src/tab.c
+++ b/libmount/src/tab.c
@@ -48,6 +48,7 @@
* will return the first entry (if UUID matches with the device).
*/
#include <blkid.h>
+#include <stdlib.h>
#include "mountP.h"
#include "strutils.h"
@@ -126,12 +127,22 @@ int mnt_reset_table(struct libmnt_table *tb)
* @tb: table pointer
*
* Increments reference counter.
+ * Calls abort() if reference counter equals SIZE_MAX prior to the increment.
*/
void mnt_ref_table(struct libmnt_table *tb)
{
if (tb) {
tb->refcount++;
- /*DBG(FS, ul_debugobj(tb, "ref=%d", tb->refcount));*/
+ /*DBG(FS, ul_debugobj(tb, "ref=%zu", tb->refcount));*/
+ /*
+ * tb->refcount == 0 now if and only if
+ * tb->refcount == SIZE_MAX prior to the increment.
+ *
+ * Checking for overflow after the increment instead of before
+ * seems to generate better assembly.
+ */
+ if (tb->refcount == 0)
+ abort();
}
}
@@ -146,8 +157,8 @@ void mnt_unref_table(struct libmnt_table *tb)
{
if (tb) {
tb->refcount--;
- /*DBG(FS, ul_debugobj(tb, "unref=%d", tb->refcount));*/
- if (tb->refcount <= 0)
+ /*DBG(FS, ul_debugobj(tb, "unref=%zu", tb->refcount));*/
+ if (tb->refcount == 0)
mnt_free_table(tb);
}
}
@@ -169,7 +180,7 @@ void mnt_free_table(struct libmnt_table *tb)
return;
mnt_reset_table(tb);
- DBG(TAB, ul_debugobj(tb, "free [refcount=%d]", tb->refcount));
+ DBG(TAB, ul_debugobj(tb, "free [refcount=%zu]", tb->refcount));
mnt_unref_cache(tb->cache);
free(tb->comm_intro);
diff --git a/libsmartcols/src/column.c b/libsmartcols/src/column.c
index a36e54690..f29475db2 100644
--- a/libsmartcols/src/column.c
+++ b/libsmartcols/src/column.c
@@ -51,11 +51,22 @@ struct libscols_column *scols_new_column(void)
* @cl: a pointer to a struct libscols_column instance
*
* Increases the refcount of @cl.
+ * Calls abort() if reference counter equals SIZE_MAX prior to the increment.
*/
void scols_ref_column(struct libscols_column *cl)
{
- if (cl)
+ if (cl) {
cl->refcount++;
+ /*
+ * cl->refcount == 0 now if and only if
+ * cl->refcount == SIZE_MAX prior to the increment.
+ *
+ * Checking for overflow after the increment instead of before
+ * seems to generate better assembly.
+ */
+ if (cl->refcount == 0)
+ abort();
+ }
}
/**
@@ -67,7 +78,7 @@ void scols_ref_column(struct libscols_column *cl)
*/
void scols_unref_column(struct libscols_column *cl)
{
- if (cl && --cl->refcount <= 0) {
+ if (cl && --cl->refcount == 0) {
DBG(COL, ul_debugobj(cl, "dealloc"));
list_del(&cl->cl_columns);
scols_reset_cell(&cl->header);
diff --git a/libsmartcols/src/filter.c b/libsmartcols/src/filter.c
index 4923cd802..a9254d9cd 100644
--- a/libsmartcols/src/filter.c
+++ b/libsmartcols/src/filter.c
@@ -60,13 +60,24 @@ struct libscols_filter *scols_new_filter(const char *str)
* @fltr: filter instance
*
* Increment filter reference counter.
+ * Calls abort() if reference counter equals SIZE_MAX prior to the increment.
*
* Since: 2.40
*/
void scols_ref_filter(struct libscols_filter *fltr)
{
- if (fltr)
+ if (fltr) {
fltr->refcount++;
+ /*
+ * fltr->refcount == 0 now if and only if
+ * fltr->refcount == SIZE_MAX prior to the increment.
+ *
+ * Checking for overflow after the increment instead of before
+ * seems to generate better assembly.
+ */
+ if (fltr->refcount == 0)
+ abort();
+ }
}
static void reset_filter(struct libscols_filter *fltr)
@@ -112,7 +123,7 @@ static void remove_counters(struct libscols_filter *fltr)
*/
void scols_unref_filter(struct libscols_filter *fltr)
{
- if (fltr && --fltr->refcount <= 0) {
+ if (fltr && --fltr->refcount == 0) {
DBG(FLTR, ul_debugobj(fltr, "dealloc"));
reset_filter(fltr);
remove_counters(fltr);
@@ -149,10 +160,27 @@ void filter_unref_node(struct filter_node *n)
}
}
+/**
+ * filter_ref_node:
+ * @n: filter_node instance
+ *
+ * Increments reference counter.
+ * Calls abort() if reference counter equals SIZE_MAX prior to the increment.
+ */
void filter_ref_node(struct filter_node *n)
{
- if (n)
+ if (n) {
n->refcount++;
+ /*
+ * n->refcount == 0 now if and only if
+ * n->refcount == SIZE_MAX prior to the increment.
+ *
+ * Checking for overflow after the increment instead of before
+ * seems to generate better assembly.
+ */
+ if (n->refcount == 0)
+ abort();
+ }
}
void filter_dump_node(struct ul_jsonwrt *json, struct filter_node *n)
diff --git a/libsmartcols/src/grouping.c b/libsmartcols/src/grouping.c
index 7e004b647..7445a7fb4 100644
--- a/libsmartcols/src/grouping.c
+++ b/libsmartcols/src/grouping.c
@@ -1,6 +1,8 @@
/*
* Copyright (C) 2018 Karel Zak <kzak@redhat.com>
*/
+#include <stdlib.h>
+
#include "smartcolsP.h"
/**
@@ -15,10 +17,28 @@
*/
/* Private API */
+
+/**
+ * scols_ref_group:
+ * @gr: group instance
+ *
+ * Increments reference counter.
+ * Calls abort() if reference counter equals SIZE_MAX prior to the increment.
+ */
void scols_ref_group(struct libscols_group *gr)
{
- if (gr)
+ if (gr) {
gr->refcount++;
+ /*
+ * gr->refcount == 0 now if and only if
+ * gr->refcount == SIZE_MAX prior to the increment.
+ *
+ * Checking for overflow after the increment instead of before
+ * seems to generate better assembly.
+ */
+ if (gr->refcount == 0)
+ abort();
+ }
}
void scols_group_remove_children(struct libscols_group *gr)
@@ -61,7 +81,7 @@ void scols_group_remove_members(struct libscols_group *gr)
/* note group has to be already without members to deallocate */
void scols_unref_group(struct libscols_group *gr)
{
- if (gr && --gr->refcount <= 0) {
+ if (gr && --gr->refcount == 0) {
DBG(GROUP, ul_debugobj(gr, "dealloc"));
scols_group_remove_children(gr);
list_del(&gr->gr_groups);
diff --git a/libsmartcols/src/line.c b/libsmartcols/src/line.c
index ffefcfba0..7789ed3aa 100644
--- a/libsmartcols/src/line.c
+++ b/libsmartcols/src/line.c
@@ -56,11 +56,22 @@ struct libscols_line *scols_new_line(void)
* @ln: a pointer to a struct libscols_line instance
*
* Increases the refcount of @ln.
+ * Calls abort() if reference counter equals SIZE_MAX prior to the increment.
*/
void scols_ref_line(struct libscols_line *ln)
{
- if (ln)
+ if (ln) {
ln->refcount++;
+ /*
+ * ln->refcount == 0 now if and only if
+ * ln->refcount == SIZE_MAX prior to the increment.
+ *
+ * Checking for overflow after the increment instead of before
+ * seems to generate better assembly.
+ */
+ if (ln->refcount == 0)
+ abort();
+ }
}
/**
@@ -72,7 +83,7 @@ void scols_ref_line(struct libscols_line *ln)
*/
void scols_unref_line(struct libscols_line *ln)
{
- if (ln && --ln->refcount <= 0) {
+ if (ln && --ln->refcount == 0) {
DBG(CELL, ul_debugobj(ln, "dealloc"));
list_del(&ln->ln_lines);
list_del(&ln->ln_children);
diff --git a/libsmartcols/src/smartcolsP.h b/libsmartcols/src/smartcolsP.h
index 1e9dcec01..8e6e92d09 100644
--- a/libsmartcols/src/smartcolsP.h
+++ b/libsmartcols/src/smartcolsP.h
@@ -61,7 +61,7 @@ struct libscols_iter {
* Tree symbols
*/
struct libscols_symbols {
- int refcount;
+ size_t refcount;
char *tree_branch;
char *tree_vert;
@@ -69,7 +69,7 @@ struct libscols_symbols {
char *group_vert;
char *group_horz;
- char *group_first_member;
+ char *group_first_member;
char *group_last_member;
char *group_middle_member;
char *group_last_child;
@@ -111,7 +111,7 @@ struct libscols_wstat {
* Table column
*/
struct libscols_column {
- int refcount; /* reference counter */
+ size_t refcount; /* reference counter */
size_t seqnum; /* column index */
size_t width; /* expected column width */
@@ -182,9 +182,9 @@ enum {
#define SCOLS_GRPSET_CHUNKSIZ 3
struct libscols_group {
- int refcount;
+ size_t refcount;
- size_t nmembers;
+ size_t nmembers;
struct list_head gr_members; /* head of line->ln_group */
struct list_head gr_children; /* head of line->ln_children */
@@ -197,7 +197,7 @@ struct libscols_group {
* Table line
*/
struct libscols_line {
- int refcount;
+ size_t refcount;
size_t seqnum;
void *userdata;
@@ -227,7 +227,7 @@ enum {
* The table
*/
struct libscols_table {
- int refcount;
+ size_t refcount;
char *name; /* optional table name (for JSON) */
size_t ncols; /* number of columns */
size_t ntreecols; /* number of columns with SCOLS_FL_TREE */
@@ -527,7 +527,7 @@ enum filter_etype {
struct filter_node {
enum filter_ntype type;
- int refcount;
+ size_t refcount;
};
#define filter_node_get_type(n) (((struct filter_node *)(n))->type)
@@ -549,7 +549,7 @@ struct libscols_counter {
};
struct libscols_filter {
- int refcount;
+ size_t refcount;
char *errmsg;
struct filter_node *root;
FILE *src;
diff --git a/libsmartcols/src/symbols.c b/libsmartcols/src/symbols.c
index 2fadfc7ad..9acb62fa3 100644
--- a/libsmartcols/src/symbols.c
+++ b/libsmartcols/src/symbols.c
@@ -43,11 +43,22 @@ struct libscols_symbols *scols_new_symbols(void)
* @sy: a pointer to a struct libscols_symbols instance
*
* Increases the refcount of @sy.
+ * Calls abort() if reference counter equals SIZE_MAX prior to the increment.
*/
void scols_ref_symbols(struct libscols_symbols *sy)
{
- if (sy)
+ if (sy) {
sy->refcount++;
+ /*
+ * sy->refcount == 0 now if and only if
+ * sy->refcount == SIZE_MAX prior to the increment.
+ *
+ * Checking for overflow after the increment instead of before
+ * seems to generate better assembly.
+ */
+ if (sy->refcount == 0)
+ abort();
+ }
}
/**
@@ -58,7 +69,7 @@ void scols_ref_symbols(struct libscols_symbols *sy)
*/
void scols_unref_symbols(struct libscols_symbols *sy)
{
- if (sy && --sy->refcount <= 0) {
+ if (sy && --sy->refcount == 0) {
free(sy->tree_branch);
free(sy->tree_vert);
free(sy->tree_right);
diff --git a/libsmartcols/src/table.c b/libsmartcols/src/table.c
index aa7cdf3c6..9d60519e5 100644
--- a/libsmartcols/src/table.c
+++ b/libsmartcols/src/table.c
@@ -96,11 +96,22 @@ struct libscols_table *scols_new_table(void)
* @tb: a pointer to a struct libscols_table instance
*
* Increases the refcount of @tb.
+ * Calls abort() if reference counter equals SIZE_MAX prior to the increment.
*/
void scols_ref_table(struct libscols_table *tb)
{
- if (tb)
+ if (tb) {
tb->refcount++;
+ /*
+ * tb->refcount == 0 now if and only if
+ * tb->refcount == SIZE_MAX prior to the increment.
+ *
+ * Checking for overflow after the increment instead of before
+ * seems to generate better assembly.
+ */
+ if (tb->refcount == 0)
+ abort();
+ }
}
static void scols_table_remove_groups(struct libscols_table *tb)
@@ -123,7 +134,7 @@ static void scols_table_remove_groups(struct libscols_table *tb)
*/
void scols_unref_table(struct libscols_table *tb)
{
- if (tb && (--tb->refcount <= 0)) {
+ if (tb && (--tb->refcount == 0)) {
DBG(TAB, ul_debugobj(tb, "dealloc <-"));
scols_table_remove_groups(tb);
scols_table_remove_lines(tb);
diff --git a/misc-utils/lsblk-devtree.c b/misc-utils/lsblk-devtree.c
index 5e9d38da1..1736c82ed 100644
--- a/misc-utils/lsblk-devtree.c
+++ b/misc-utils/lsblk-devtree.c
@@ -16,11 +16,12 @@
*
* Copyright (C) 2018 Karel Zak <kzak@redhat.com>
*/
+#include <stdlib.h>
+
#include "lsblk.h"
#include "sysfs.h"
#include "pathnames.h"
-
void lsblk_reset_iter(struct lsblk_iter *itr, int direction)
{
if (direction == -1)
@@ -51,10 +52,27 @@ struct lsblk_device *lsblk_new_device(void)
return dev;
}
+/**
+ * lsblk_ref_device:
+ * @dev: a pointer to a struct lsblk_device instance
+ *
+ * Increments the refcount of @dev.
+ * Calls abort() if reference counter equals SIZE_MAX prior to the increment.
+ */
void lsblk_ref_device(struct lsblk_device *dev)
{
- if (dev)
+ if (dev) {
dev->refcount++;
+ /*
+ * dev->refcount == 0 now if and only if
+ * dev->refcount == SIZE_MAX prior to the increment.
+ *
+ * Checking for overflow after the increment instead of before
+ * seems to generate better assembly.
+ */
+ if (dev->refcount == 0)
+ abort();
+ }
}
/* removes dependence from child as well as from parent */
@@ -101,7 +119,7 @@ void lsblk_unref_device(struct lsblk_device *dev)
if (!dev)
return;
- if (--dev->refcount <= 0) {
+ if (--dev->refcount == 0) {
DBG(DEV, ul_debugobj(dev, " freeing [%s] <<", dev->name));
device_remove_dependences(dev);
@@ -251,10 +269,27 @@ struct lsblk_devtree *lsblk_new_devtree(void)
return tr;
}
+/**
+ * lsblk_ref_devtree:
+ * @tr: a pointer to a struct lsblk_devtree instance
+ *
+ * Increments the refcount of @tr.
+ * Calls abort() if reference counter equals SIZE_MAX prior to the increment.
+ */
void lsblk_ref_devtree(struct lsblk_devtree *tr)
{
- if (tr)
+ if (tr) {
tr->refcount++;
+ /*
+ * tr->refcount == 0 now if and only if
+ * tr->refcount == SIZE_MAX prior to the increment.
+ *
+ * Checking for overflow after the increment instead of before
+ * seems to generate better assembly.
+ */
+ if (tr->refcount == 0)
+ abort();
+ }
}
void lsblk_unref_devtree(struct lsblk_devtree *tr)
@@ -262,7 +297,7 @@ void lsblk_unref_devtree(struct lsblk_devtree *tr)
if (!tr)
return;
- if (--tr->refcount <= 0) {
+ if (--tr->refcount == 0) {
DBG(TREE, ul_debugobj(tr, "dealloc"));
while (!list_empty(&tr->devices)) {
diff --git a/misc-utils/lsblk.h b/misc-utils/lsblk.h
index 90d2df1a1..f51949ec0 100644
--- a/misc-utils/lsblk.h
+++ b/misc-utils/lsblk.h
@@ -124,7 +124,7 @@ struct lsblk_devdep {
};
struct lsblk_device {
- int refcount;
+ size_t refcount;
struct list_head childs; /* list with lsblk_devdep */
struct list_head parents;
@@ -193,7 +193,7 @@ struct lsblk_devnomap {
* md0 -> sda1 -> sda
*/
struct lsblk_devtree {
- int refcount;
+ size_t refcount;
struct list_head roots; /* tree root devices */
struct list_head devices; /* all devices */
diff --git a/sys-utils/lscpu-cpu.c b/sys-utils/lscpu-cpu.c
index 0619e7d34..1f306bb5d 100644
--- a/sys-utils/lscpu-cpu.c
+++ b/sys-utils/lscpu-cpu.c
@@ -8,6 +8,8 @@
*
* Copyright (C) 2020 Karel Zak <kzak@redhat.com>
*/
+#include <stdlib.h>
+
#include "lscpu.h"
struct lscpu_cpu *lscpu_new_cpu(int id)
@@ -28,10 +30,27 @@ struct lscpu_cpu *lscpu_new_cpu(int id)
return cpu;
}
+/**
+ * lscpu_ref_cpu:
+ * @cpu: a pointer to a struct lscpu_cpu instance
+ *
+ * Increments the refcount of @cpu.
+ * Calls abort() if reference counter equals SIZE_MAX prior to the increment.
+ */
void lscpu_ref_cpu(struct lscpu_cpu *cpu)
{
- if (cpu)
+ if (cpu) {
cpu->refcount++;
+ /*
+ * cpu->refcount == 0 now if and only if
+ * cpu->refcount == SIZE_MAX prior to the increment.
+ *
+ * Checking for overflow after the increment instead of before
+ * seems to generate better assembly.
+ */
+ if (cpu->refcount == 0)
+ abort();
+ }
}
void lscpu_unref_cpu(struct lscpu_cpu *cpu)
@@ -39,7 +58,7 @@ void lscpu_unref_cpu(struct lscpu_cpu *cpu)
if (!cpu)
return;
- if (--cpu->refcount <= 0) {
+ if (--cpu->refcount == 0) {
DBG(CPU, ul_debugobj(cpu, " freeing #%d", cpu->logical_id));
lscpu_unref_cputype(cpu->type);
cpu->type = NULL;
diff --git a/sys-utils/lscpu-cputype.c b/sys-utils/lscpu-cputype.c
index 958cbe7b6..9dffb8df9 100644
--- a/sys-utils/lscpu-cputype.c
+++ b/sys-utils/lscpu-cputype.c
@@ -8,6 +8,7 @@
*
* Copyright (C) 2020 Karel Zak <kzak@redhat.com>
*/
+#include <stdlib.h>
#include <sys/utsname.h>
#include <sys/personality.h>
@@ -69,11 +70,27 @@ struct lscpu_cputype *lscpu_new_cputype(void)
return ct;
}
+/**
+ * lscpu_ref_cputype:
+ * @ct: a pointer to a struct lscpu_cputype instance
+ *
+ * Increments the refcount of @ct.
+ * Calls abort() if reference counter equals SIZE_MAX prior to the increment.
+ */
void lscpu_ref_cputype(struct lscpu_cputype *ct)
{
if (ct) {
ct->refcount++;
- /*DBG(TYPE, ul_debugobj(ct, ">>> ref %d", ct->refcount));*/
+ /*DBG(TYPE, ul_debugobj(ct, ">>> ref %zu", ct->refcount));*/
+ /*
+ * ct->refcount == 0 now if and only if
+ * ct->refcount == SIZE_MAX prior to the increment.
+ *
+ * Checking for overflow after the increment instead of before
+ * seems to generate better assembly.
+ */
+ if (ct->refcount == 0)
+ abort();
}
}
@@ -82,9 +99,9 @@ void lscpu_unref_cputype(struct lscpu_cputype *ct)
if (!ct)
return;
- /*DBG(TYPE, ul_debugobj(ct, ">>> unref %d", ct->refcount - 1));*/
+ /*DBG(TYPE, ul_debugobj(ct, ">>> unref %zu", ct->refcount - 1));*/
- if (--ct->refcount <= 0) {
+ if (--ct->refcount == 0) {
DBG(TYPE, ul_debugobj(ct, " freeing %s/%s", ct->vendor, ct->model));
lscpu_cputype_free_topology(ct);
free(ct->vendor);
diff --git a/sys-utils/lscpu.h b/sys-utils/lscpu.h
index 0fae5d29e..5f5a0315a 100644
--- a/sys-utils/lscpu.h
+++ b/sys-utils/lscpu.h
@@ -13,6 +13,7 @@
#define LSCPU_H
#include <stdbool.h>
+#include <stddef.h>
#include "c.h"
#include "nls.h"
@@ -67,7 +68,7 @@ struct lscpu_cache {
};
struct lscpu_cputype {
- int refcount;
+ size_t refcount;
char *vendor;
int vendor_id; /* created by lscpu_decode_arm() */
@@ -137,7 +138,7 @@ enum {
};
struct lscpu_cpu {
- int refcount;
+ size_t refcount;
struct lscpu_cputype *type;
int logical_id;
--
2.53.0
next reply other threads:[~2026-02-24 10:23 UTC|newest]
Thread overview: 2+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-02-24 10:23 Jonathan Gruber [this message]
2026-02-25 10:18 ` [PATCH] Various subsystems: avoid UB from ref counts Karel Zak
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260224102329.69255-1-jonathan.gruber.jg@gmail.com \
--to=jonathan.gruber.jg@gmail.com \
--cc=util-linux@vger.kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox