Mismatched new/free in std::logic_error

lsb_release -a

Distributor ID: Ubuntu
Description: Ubuntu 22.04.1 LTS
Release: 22.04
Codename: jammy

throw std::invalid_argument(fmt::format(
        "Offset '{}' in block {} is too big.", offset, blk.metadata()->id));

later…

 REQUIRE_THROWS_AS(insert(b, ectx, 10000, dom::text_attribute{}, test_text),
                    std::invalid_argument);

Address sanitizer reports that the std::invalid_argument constructor uses “operator new” to allocate the string message, and “free” to release it in ~std::logic_error.

This seems odd, because invalid_argument delegates to logic_error, which delegates to exception:

explicit logic_error(const char* __s) : exception(__s) {}

However, exception (in <exception>) does not even appear to define such a constructor. Any help to understand this problem and how to resolve it would be greatly appreciated!

Can you provide a self-contained reproducer? What I’m seeing here is that ~logic_error is destroying the __libcpp_refstring it holds, which is using ::operator delete:

__libcpp_refstring::~__libcpp_refstring() {
    if (__uses_refcount()) {
        _Rep_base* rep = rep_from_data(__imp_);
        if (__libcpp_atomic_add(&rep->count, count_t(-1)) < 0) {
            ::operator delete(rep);
        }
    }
}

I tried a minimal reproducer, and of course it worked. I think this may be happening when the linker is involved. I will try to make a reproducer with a static library and see what happens.