It seems that valid inode can be 64-bit and negative (or rather outside
of 64-bit signed range)
https://github.com/freebsd/freebsd-src/blob/
a60615d5be83ca050d4ddfbbb4411ca7a8a11486/sys/sys/_types.h#L124
https://github.com/torvalds/linux/blob/
7e74f756f5f643148ca5537bf2fee6767e4b0ed9/include/linux/types.h#L22
But signed type is used in libarchive and there were some fuzzing issues
with it, https://github.com/libarchive/libarchive/pull/2258 converts
negative `ino` to `0`, which is actually a reserved inode value, but
more importantly it was still setting `AE_SET_INO` flag and later on
hardlink detection will treat all `0` on same `dev` as hardlinks to each
other if they have some hardlinks.
This showed up in BuildBarn FUSE filesystem
https://github.com/buildbarn/bb-remote-execution/issues/162 which has
both
- setting number of links to a big value
- generating random inode values in full uint64 range
Which in turn triggers false-positive hardlink detection in `bsdtar`
with high probability.
Let's mitigate it
- don't set `AE_SET_INO` on negative values (assuming rest of code isn't
ready to correctly handle full uint64 range)
- check that `ino + dev` are set in link resolver
archive_entry_set_ino(struct archive_entry *entry, la_int64_t ino)
{
if (ino < 0) {
- ino = 0;
+ entry->stat_valid = 0;
+ entry->ae_set &= ~AE_SET_INO;
+ return;
}
entry->stat_valid = 0;
entry->ae_set |= AE_SET_INO;
archive_entry_set_ino64(struct archive_entry *entry, la_int64_t ino)
{
if (ino < 0) {
- ino = 0;
+ entry->stat_valid = 0;
+ entry->ae_set &= ~AE_SET_INO;
+ return;
}
entry->stat_valid = 0;
entry->ae_set |= AE_SET_INO;
dev_t dev;
int64_t ino;
+ if (!archive_entry_ino_is_set(entry) || !archive_entry_dev_is_set(entry)) {
+ return (NULL);
+ }
+
/* Free a held entry. */
if (res->spare != NULL) {
archive_entry_free(res->spare->canonical);
struct links_entry *le;
size_t hash, bucket;
+ if (!archive_entry_ino_is_set(entry) || !archive_entry_dev_is_set(entry)) {
+ return (NULL);
+ }
+
/* Add this entry to the links cache. */
le = calloc(1, sizeof(struct links_entry));
if (le == NULL)