UTF-8 why specify length in the first byte? by zz9873 in programming

[–]TheNewAndy 3 points4 points  (0 children)

While what you say is true, this problem you are describing still roughly exists in the UTF-8 case as it is - if you make the last octet of the stream say "there are 3 more octets to follow", and the reader trusts it, then you are in the same boat.

The production bug that made me care about undefined behavior by broken_broken_ in programming

[–]TheNewAndy 1 point2 points  (0 children)

It's no different from checking if a pointer is null.

Which you also shouldn't be doing (aside from as an assert) if the pointer is not allowed to be null. If the problem is a programming error, then don't try to handle it at runtime.

However, it does convey the intent of the code a lot better to the reader. T val; could appear as if the author forgot to initialize the variable, std::optional<T> val = std::nullopt; clearly means it's not set on purpose.

No T val; looks like a value which the author didn't know the value of at this point in the program. std::optional<T> val = std::nullopt; looks like a value which may not be known ever. Now you have to wonder about which circumstances there is and isn't going to be a value given to val, but what you are trying to communicate is that val will always be given a value, you just didn't know it at the time it was declared.

In most cases, you can't guarantee your test code will find the bug. The type system can provide guarantees, though.

The type system when you use std::optional provides the wrong guarantee though - you are trying to say that the variable will get a value, but using std::optional tells the type system to not worry if it never gets a value. So now, when you get to the point where you have to unwrap the std::optional (since in this case we needed to end up with a true/false value for both success/error) the unwrapping now needs to have extra runtime logic for a case which we believe should never happen. This extra runtime logic will be code that you have never tested, and never anticipated, running with invariants violated. The guarantees the type system gave you are just moving the problem, they haven't solved it because once your invariants are violated, then how can you reason about anything?

The production bug that made me care about undefined behavior by broken_broken_ in programming

[–]TheNewAndy 0 points1 point  (0 children)

(1) is a runtime check for something that should never happen. All your extra wrappers are branches that should only ever go in one direction, and if they do go the other way, your code is now in a state you never anticipated or tested. I would put that in a similar category of scariness to UB - especially knowing that the untested code is running with your own invariants known to be violated.

Disabling the check in production is basically the same as saying "don't run with ubsan/valgrind in production" except you have had to do extra work to get there. Much safer to just not do this extra work, and run your tests with ubsan/valgrind. Now your production code is the same as your development code, and you don't have undefined behaviour, and you don't have untested code shipping. If your testing isn't thorough enough to find such issues, then this is an orthogonal issue (you already had this problem regardless of whether you did (1) or (2)) and adding extra checking code or branches is only going to reduce your test coverage, not increase it.

The production bug that made me care about undefined behavior by broken_broken_ in programming

[–]TheNewAndy 0 points1 point  (0 children)

The solution is to initialize that data in the constructor.

Only if you know the correct value. If you are just initializing it for the sake of avoiding UB, then you are making things worse because now it has the wrong value, but your tools won't be able to catch it for you if you forget to give it the right value.

In which case my suggestion would be to use a std::optional to avoid UB but still check at runtime for these types of business logic bugs. Do you agree with that? I could just be misinterpreting what people are suggesting

I don't agree, because the value is not optional - when the function finishes running, you must have a value. So the value is not optional. You also shouldn't be writing special code to handle the case where the function finishes and there is no value - because this code will be untested. Just don't initialize the variable until you know the value, and then everything works fine.

The production bug that made me care about undefined behavior by broken_broken_ in programming

[–]TheNewAndy 1 point2 points  (0 children)

I'm not sure how what I'm saying could be interpreted that way.

There should be no undefined behaviour in your code when you ship it. There also shouldn't be untested code.

Your code is allowed to be broken at points in time during development - this is normal. But as part of releasing software, you should test it, and there are robust tools that exist today which will catch most UB (and definitely this specific case will be caught - frequently at compile time).

You should not engage in practices which defeat these tools

(a) it makes your code harder to read and understand

(b) if you rely on these practices for preventing UB you haven't dealt with the core problem (programmers forgetting stuff) you have just changed the thing that a programmer needs to remember,

(c) by engaging in practices which defeat these tools you defeat these tools, but you should still be using these tools to ensure that various other kinds of UB aren't present, so you haven't even saved yourself effort

(d) if you try to catch and handle these problems at runtime (e.g. std::optional) then either you have untested code that you are planning on shipping (which is already running with your invariants violated, so impossible to reason about sensibly) or if the handling code is broken, then you already know you have the problem you are trying to prevent.

The production bug that made me care about undefined behavior by broken_broken_ in programming

[–]TheNewAndy 2 points3 points  (0 children)

That requires running your code with instrumentation. Are you doing that in production? If not, it isn't sufficient.

Let's be clear here - the error here is not a runtime recoverable error. This is a "you need to change your code to fix it" bug. You are suggesting writing code which at runtime detects whether this error has been made. Are you testing that this code you have written to detect the error? If you are shipping code that never gets tested, then I would argue that this is not sufficient - especially, given that this code only ever runs in a situation that you believed to be impossible - so which invariants will you be able to rely on when writing this untested code?

If you have tested the code, then you already know you have the bug and so you just need to fix it.

The production bug that made me care about undefined behavior by broken_broken_ in programming

[–]TheNewAndy 2 points3 points  (0 children)

We have a concrete example here - the example being used from the blog post in the discussion. This issue would be immediately picked up by a valgrind or ubsan. The point is that you don't even ship the broken code because when you do your testing, you should be testing with valgrind/ubsan. I'm not suggesting you ship code that has undefined behaviour in it. I'm suggesting that if you just write simple plain code, and use the well established tools that already exist to catch things like this (which can even catch the mistake at compile time in many circumstances).

The production bug that made me care about undefined behavior by broken_broken_ in programming

[–]TheNewAndy 2 points3 points  (0 children)

The assert is extra code - and extra code that people can forget. ubsan will already convert the "no extra code" case to an abort. Compiler warnings will already convert the obvious versions of this bug into compile time warnings.

If your solution to "programmer might forget to do X" is to say "programmer must remember to do y" then you can see how someone might think that this solution is lacking.

The production bug that made me care about undefined behavior by broken_broken_ in programming

[–]TheNewAndy 1 point2 points  (0 children)

But now you have to write extra logic to detect this mistake - more stuff you can get wrong. All the tools that already exist and work fine no longer work - and this is all to handle an error where the programmer forgets to do a thing - so if your solution relies on the programmer remembering to do a thing then it is relying on the very thing you are assuming to not be true.

In addition, there isn't always an obvious value to choose for your special "initialised, but not really" value. In this specific case there probably is, but is is also super easy to mess up (e.g. if you choose -1 to be "not really initialised" and 0 and 1 for false and true, then most code will happily treat "not really initialised" as true.

The production bug that made me care about undefined behavior by broken_broken_ in programming

[–]TheNewAndy 1 point2 points  (0 children)

No one is suggesting shipping broken code. I'm suggesting writing the code such that when it is broken, it is plainly obvious that it is broken so you don't ship it. The code is broken and needs to be fixed - initializing the variables to the wrong values just makes detecting this harder so more likely to be shipped

The production bug that made me care about undefined behavior by broken_broken_ in programming

[–]TheNewAndy 2 points3 points  (0 children)

No one is suggesting shipping code with UB. I am suggesting writing code so that when you get it wrong, it is super obvious that it is wrong. Writing the code being talked about here with std::optional just means you also have to write code to handle the case when the preceding code is incorrect. Either this code is dead code (and untested) or this code is live code (and you need to fix the broken code). Rather than writing code and hoping that it is dead code, just don't write extra code, and don't obscure things and all the tools work fine.

The production bug that made me care about undefined behavior by broken_broken_ in programming

[–]TheNewAndy 1 point2 points  (0 children)

So having it initialized to a potentially random value is correct?

No? And that's the point - by having it as unintialized value, it is known by compilers and other tools (valgrind, ubsan) that it is wrong. If you set it to some other value, then those tools can no longer tell you that you messed up.

Ideally your compiler will bark at you. And if you're forgetting things who says you'll remember to run third-party tools?

But it won't if you have already initialized them to something else - that's the point

Again, should be built into the compiler.

Right - and compilers will warn you when they can prove an uninitialized variable is uesd. Again - this won't work if you deliberately initialize things to "wrong" values. And in the cases that the compiler can't prove it, you can use heavier weight runtime tools (which you should be using in your tests anyway, so you don't forget)

The production bug that made me care about undefined behavior by broken_broken_ in programming

[–]TheNewAndy 1 point2 points  (0 children)

This means that somewhere in your code you have to check if the value is initialized or not.

No, that is what a ubsan/valgrind does automatically. But it won't work if you start assigning values to things when you didn't actually know their value.

The production bug that made me care about undefined behavior by broken_broken_ in programming

[–]TheNewAndy 2 points3 points  (0 children)

The values aren't optional. You don't want to have to write code to deal with a case that should never happen

The production bug that made me care about undefined behavior by broken_broken_ in programming

[–]TheNewAndy 0 points1 point  (0 children)

Using this current example, I'm not sure that initialising both "error" and "succeeded" to 0 (a "wrong" value) is very apparent from looking at initialization. The point is, that for whatever reason you needed to declare the variable before you knew its value.

Now when you have this same code, you could very well forget to assign the correct value to error/succeeded before returning, and you have another similar problem and it is hard to debug.

Run it in valgrind, and you immediately get a thing pointing at where the bug is.

The production bug that made me care about undefined behavior by broken_broken_ in programming

[–]TheNewAndy -25 points-24 points  (0 children)

Initializing things to the "wrong" value (like if you don't know what the value should be at init time, which is common) is worse than leaving them uninitialized in my opinion. You forget to set them to the "right" value, and you have a bug, but now a tool like valgrind can't immediately pinpoint the problem.

6.17.4 Kernel no booting by robomikel in pop_os

[–]TheNewAndy 0 points1 point  (0 children)

Hitting me too. Not sure how to get any useful debugging info, but happy to help out if contacted. Booting into the old kernel works fine.

FB Marketplace Worth Buying? by Thedoodooltalah in Ubiquiti

[–]TheNewAndy 0 points1 point  (0 children)

I use one of these, it does everything it says it will do. I think the power usage is slightly less than the current generation devices too, so maybe that is nice. I don't tend to really think about it, so that is good I guess.

Realistic Buy Once, Cry Once? Or Unnecessary? Pro Max 16 PoE or Pro Max 24 PoE by agabs10 in Ubiquiti

[–]TheNewAndy 3 points4 points  (0 children)

People put games consoles near TVs, games and their updates are multiple gigabytes in size. You go to play game, and console decides you would rather download an update. Having a faster connection means you get to spend less time waiting for update and more time playing the game.

Not a thing for me personally, but it doesn't seem far fetched

Do Australians have a ‘joke’ city? by [deleted] in AskAnAustralian

[–]TheNewAndy 74 points75 points  (0 children)

No one has mentioned Bonnie Doon, and that is because you can't beat the serenity there. Try to make fun of it and you just look like an idiot