Asserts should only be used to signal "this code is broken" but that is not how they are used here.
A long while ago the GTK+ library used to suffer from a similar affliction. Mainstay GTK+ programs such as GEdit and Totem would print lots of warnings on the console. Since the GTK+ developers used the same facility (g_warning) for reporting both coding errors and for user errors like "file not found," debugging code was hard. Was the warning "supposed to be there" or was it a bug? GTK+ programs are much cleaner these days even if some programs still fails to make the distinction between user and programmer errors.
Assert already has a meaning and using it in this context is not great.
i.e. "assert" meaning "check something in this code is algorithmically correct"
Whereas when you're checking user input, use a very different term - i.e. validate or something.
So a fix would be to rename the macro to be obvious about it's usage - "validate_return" or "valid_or_return" or something.
To be fair I can see the problem because I do tend to write code comments saying "assert everything makes sense" when checking user input sometimes and "assert" itself doesn't properly convey the outcome if it fails - i.e. "abort_if" or "crash_if" would be a much more descriptive term.
If assert isn't assert, and if return values are also abused, why can I assume get is get, sort is sort and so on? Sort might check inputs, get might delete the customer account, who knows at this point. And that makes anything but a tiny code base pretty much unmanageable, or at least very very hard to learn.
At one point and older coder who I looked up to even complemented and adopted this pattern. 1998 was a good year.
I sometimes use a bunch of Ensure*() methods in C# if I need to validate input data. They throw exceptions if condition fails. If I only do this once I don't create those methods.
One drawback is the stacktrace has these Ensure names in it.
I would go so far as to say that using asserts in input validation is a good thing, in very limited quantities. I use them in places where the input should have been validated so long ago, so many functions back, that if something isn't as I expect here, I seriously fucked up somewhere else and the whole system has a good likelihood of being compromised by something unrelated to the condition I'm asserting. In that scenario I'd rather log and abort.
In systemd-networkd, the "asserts" assert the pre-conditions of a successful call to that function (in dhcp6-option.c). It's up to the caller (in sd-dhcp6-lease.c) to validate the users input, and ensure that it's passing valid arguments to the functions it's calling.
Building software in layers, and making use of "internal" libraries is a valid software engineering strategy for managing complexity.
Because, unfortunately, POSIX requires it to do that. But that's a bad decision on the part of POSIX, and we shouldn't perpetuate that when we have the choice. Assertions that can be "handled" are not assertions - they're errors. By returning an error code, you're saying "it's okay to pass bad things here without checking, and check the error code afterwards". If it's not actually okay, because validation is not reliable, then you're just enabling writing bad, unreliable code that shouldn't even be possible to write.
The "bad descriptor" check in read() is a good example. You can never rely on EBADF unless you fully control every line of code that ever runs in your process, because a descriptor might be accidentally valid - it might be a descriptor that was closed, but then got reused for something else (and POSIX explicitly requires reuse of file descriptors). So checking for EBADF to e.g. detect closed descriptors and do something about that will mostly work, but it will sporadically break by doing something on the wrong descriptor. Therefore, it would be strictly better if read() just called abort(), and thereby forced the caller to use some other mechanism to keep track of what's valid and what isn't - the one that the caller can implement reliably, if it can.
The code in question quite clearly doesn't use C's standard assert macro, but a custom macro called assert_return (which I guess checks for a condition, and returns from the function instead of aborting the program).
So basically:
How else would one check for invalid input to the function?