kernel: best-effort IdString provenance checking

This commit is contained in:
Emil J. Tywoniak
2026-10-02 18:42:01 +02:00
parent 52faadc45a
commit d1de31d2c3
2 changed files with 134 additions and 16 deletions
+82 -6
View File
@@ -21,7 +21,7 @@ bool StaticTwines::ready() { return nodes_.size() == count; }
int64_t twine_gc_ns;
int twine_gc_count;
Hasher IdString::hash_into(Hasher h) const { h.hash64(value); return h; }
Hasher IdString::hash_into(Hasher h) const { h.hash64(raw()); return h; }
std::string IdString::handle_token() const {
return stringf("%s@%zu", isPublic() ? "$pub" : "$priv", untag().raw());
@@ -53,9 +53,16 @@ std::string ID::unescaped_str(IdString ref) {
return static_names[idx.raw()];
}
Twine::Twine(Leaf v) : data(std::move(v)) {}
Twine::Twine(Suffix v) : data(std::move(v)) {}
Twine::Twine(AutoSuffix v) : data(std::move(v)) {}
bool Twine::is_leaf() const { return std::holds_alternative<Leaf>(data); }
bool Twine::is_suffix() const { return std::holds_alternative<Suffix>(data); }
TwineNode::TwineNode(Twine::Leaf v) : data(std::move(v)) {}
TwineNode::TwineNode(Twine::Suffix v) : data(std::move(v)) {}
bool TwineNode::is_dead() const { return std::holds_alternative<std::monostate>(data); }
bool TwineNode::is_leaf() const { return std::holds_alternative<Twine::Leaf>(data); }
bool TwineNode::is_suffix() const { return std::holds_alternative<Twine::Suffix>(data); }
@@ -76,12 +83,62 @@ std::pair<std::string, bool> twine_unescape(std::string s) {
return {std::move(s), is_public};
}
TwinePool::TwinePool() : serial_(next_serial()) {}
TwinePool::TwinePool(const TwinePool& other) : HashConsPool(other), serial_(next_serial()) {}
TwinePool::TwinePool(TwinePool&& other) : HashConsPool(std::move(other)), serial_(next_serial()) {}
TwinePool& TwinePool::operator=(const TwinePool& other) {
HashConsPool::operator=(other);
return *this;
}
TwinePool& TwinePool::operator=(TwinePool&& other) {
HashConsPool::operator=(std::move(other));
return *this;
}
size_t TwinePool::serial() const { return serial_; }
bool TwinePool::owns(IdString ref) const {
return ref == IdString::Null || ref.serial() == 0 || ref.serial() == serial_ || ID::is_static(ref);
}
IdString TwinePool::stamp(IdString ref) const {
if (ref == IdString::Null || ID::is_static(ref))
return ref;
return ref.stamped(serial_);
}
void TwinePool::check_owned(IdString ref) const {
#ifndef NDEBUG
log_assert(owns(ref));
#endif
}
const TwineNode& TwinePool::operator[](IdString ref) const {
check_owned(ref);
return HashConsPool::operator[](ref);
}
const TwineNode& TwinePool::static_node(size_t idx) { return StaticTwines::node(idx); }
void TwinePool::check_ready() { log_assert(StaticTwines::ready()); }
void TwinePool::canonicalize(TwineNode& t) {
if (auto *sfx = std::get_if<Twine::Suffix>(&t.data))
sfx->prefix = sfx->prefix.untag();
sfx->prefix = sfx->prefix.untag().stamped(0);
}
size_t TwinePool::next_serial() {
static size_t counter = 0;
size_t serial = ++counter;
return serial > IdString::MAX_SERIAL ? (serial % IdString::MAX_SERIAL) + 1 : serial;
}
IdString TwinePool::add_inner(TwineNode t) {
if (free_list.empty() && STATIC_COUNT + backing.size() > IdString::MAX_INDEX)
log_error("Out of twine handles: a design may name at most %zu distinct twines.\n",
IdString::MAX_INDEX - STATIC_COUNT);
return HashConsPool::add_inner(std::move(t));
}
size_t TwinePool::hash_node(const TwineNode& t) {
@@ -164,7 +221,7 @@ std::string TwinePool::unescaped_str(IdString ref) const {
IdString TwinePool::find(const std::string &name) const {
bool is_public = !name.empty() && name[0] == '\\';
return find(Twine::Leaf{is_public ? name.substr(1) : name}).tag(is_public);
return stamp(find(Twine::Leaf{is_public ? name.substr(1) : name}).tag(is_public));
}
IdString TwinePool::find(Twine t) const {
@@ -175,7 +232,7 @@ IdString TwinePool::find(Twine t) const {
t = Twine::Suffix{prefix, std::move(ap->tail)};
}
bool is_public = inherits_publicity(t);
return HashConsPool::find(to_node(std::move(t))).tag(is_public);
return stamp(HashConsPool::find(to_node(std::move(t))).tag(is_public));
}
IdString TwinePool::add(Twine t) {
@@ -184,14 +241,14 @@ IdString TwinePool::add(Twine t) {
t = Twine::Suffix{prefix, std::move(ap->tail)};
}
bool is_public = inherits_publicity(t);
return add_inner(to_node(std::move(t))).tag(is_public);
return stamp(add_inner(to_node(std::move(t))).tag(is_public));
}
IdString TwinePool::add(std::string s) {
if (s.empty())
return IdString::Null;
auto [content, is_public] = twine_unescape(std::move(s));
return add_inner(Twine::Leaf{std::move(content)}).tag(is_public);
return stamp(add_inner(Twine::Leaf{std::move(content)}).tag(is_public));
}
IdString TwinePool::copy_from(const TwinePool& src, IdString ref) {
@@ -230,6 +287,25 @@ IdString TwinePool::find_from(const TwinePool& src, IdString ref) const {
return IdString::Null;
}
std::string TwinePool::ref_token(IdString ref) const {
return "#" + std::to_string((uint64_t)stamp(ref).bits());
}
IdString TwinePool::ref_from_token(std::string_view token) const {
if (token.size() < 2 || token[0] != '#')
return IdString::Null;
size_t value = 0;
for (char c : token.substr(1)) {
if (c < '0' || c > '9')
return IdString::Null;
value = value * 10 + (c - '0');
}
IdString ref(value);
if (ref == IdString::Null || (!ID::is_static(ref) && ref.serial() != serial_))
return IdString::Null;
return is_live(ref) ? ref : IdString::Null;
}
void TwinePool::dump(std::ostream& os) const {
os << "--- TwinePool Dump (" << backing.size() << " nodes) ---\n";
for (size_t idx = 0; idx < backing.size(); ++idx) {
+52 -10
View File
@@ -49,20 +49,42 @@ private:
static constexpr size_t TWINE_PUBLIC_BIT = 1ULL << 63;
static constexpr size_t TWINE_NULL_VAL = ~size_t{0};
static constexpr int TWINE_SERIAL_SHIFT = 32;
static constexpr size_t TWINE_INDEX_MASK = (size_t{1} << TWINE_SERIAL_SHIFT) - 1;
static constexpr size_t TWINE_SERIAL_MASK = ~(TWINE_INDEX_MASK | TWINE_PUBLIC_BIT);
public:
static constexpr NullIdString Null{};
constexpr size_t raw() const { return value; }
static constexpr size_t MAX_INDEX = TWINE_INDEX_MASK;
static constexpr size_t MAX_SERIAL = TWINE_SERIAL_MASK >> TWINE_SERIAL_SHIFT;
constexpr size_t raw() const {
return value == TWINE_NULL_VAL ? value : (value & ~TWINE_SERIAL_MASK);
}
// raw() with the pool serial kept
constexpr size_t bits() const { return value; }
constexpr size_t serial() const {
return value == TWINE_NULL_VAL ? 0 : ((value & TWINE_SERIAL_MASK) >> TWINE_SERIAL_SHIFT);
}
constexpr IdString stamped(size_t pool_serial) const {
return value == TWINE_NULL_VAL ? *this
: IdString((value & ~TWINE_SERIAL_MASK) | (pool_serial << TWINE_SERIAL_SHIFT));
}
constexpr IdString() : value(TWINE_NULL_VAL) {}
explicit constexpr IdString(size_t val) : value(val) {}
constexpr bool operator==(const IdString&) const = default;
constexpr bool operator==(const IdString &rhs) const { return raw() == rhs.raw(); }
constexpr bool operator!=(const IdString &rhs) const { return raw() != rhs.raw(); }
constexpr std::strong_ordering operator<=>(const IdString &rhs) const {
if (auto cmp = (value & ~TWINE_PUBLIC_BIT) <=> (rhs.value & ~TWINE_PUBLIC_BIT); cmp != 0)
size_t a = raw(), b = rhs.raw();
if (auto cmp = (a & ~TWINE_PUBLIC_BIT) <=> (b & ~TWINE_PUBLIC_BIT); cmp != 0)
return cmp;
return (value & TWINE_PUBLIC_BIT) <=> (rhs.value & TWINE_PUBLIC_BIT);
return (a & TWINE_PUBLIC_BIT) <=> (b & TWINE_PUBLIC_BIT);
}
template <typename... Args>
@@ -177,9 +199,9 @@ struct Twine {
std::variant<Leaf, Suffix, AutoSuffix> data;
Twine(Leaf v) : data(std::move(v)) {}
Twine(Suffix v) : data(std::move(v)) {}
Twine(AutoSuffix v) : data(std::move(v)) {}
Twine(Leaf v);
Twine(Suffix v);
Twine(AutoSuffix v);
bool is_leaf() const;
bool is_suffix() const;
@@ -191,8 +213,8 @@ struct TwineNode {
std::variant<std::monostate, Twine::Leaf, Twine::Suffix> data;
TwineNode() = default;
TwineNode(Twine::Leaf v) : data(std::move(v)) {}
TwineNode(Twine::Suffix v) : data(std::move(v)) {}
TwineNode(Twine::Leaf v);
TwineNode(Twine::Suffix v);
bool is_dead() const;
bool is_leaf() const;
@@ -220,6 +242,18 @@ std::pair<std::string, bool> twine_unescape(std::string s);
struct TwinePool : HashConsPool<TwinePool, TwineNode, IdString> {
static constexpr size_t STATIC_COUNT = StaticTwines::count;
TwinePool();
TwinePool(const TwinePool& other);
TwinePool(TwinePool&& other);
TwinePool& operator=(const TwinePool& other);
TwinePool& operator=(TwinePool&& other);
size_t serial() const;
bool owns(IdString ref) const;
IdString stamp(IdString ref) const;
void check_owned(IdString ref) const;
const TwineNode& operator[](IdString ref) const;
static const TwineNode& static_node(size_t idx);
// static IdString untag(IdString ref);
@@ -259,13 +293,21 @@ struct TwinePool : HashConsPool<TwinePool, TwineNode, IdString> {
IdString copy_from(const TwinePool& src, IdString ref);
// Non-mutating counterpart of copy_from
IdString find_from(const TwinePool& src, IdString ref) const;
// Opaque handle for files that never leave one run of yosys.
// Only valid until garbage collection reuses the slot.
std::string ref_token(IdString ref) const;
// Null unless the token names a live twine stamped by this pool
IdString ref_from_token(std::string_view token) const;
void dump(std::ostream& os = std::cout) const;
using HashConsPool::gc;
private:
IdString add_inner(TwineNode t);
static size_t next_serial();
size_t serial_;
static bool inherits_publicity(const Twine &t);
static TwineNode to_node(Twine t);
using HashConsPool::add_inner;
};
/**