|
Schrodenger's solution:code:
Still permitted to turn it into code:
code:
Foxfire_ fucked around with this message at 22:25 on Mar 7, 2020 |
# ? Mar 7, 2020 22:22 |
|
|
# ? Jun 7, 2024 23:42 |
|
How do systems with stuff mapped at 0 even work? C++ still says that a pointer created from a zero integer constant is a null pointer that you can't dereference, do you have to go out of your way to decrement a pointer to address 1 or something?
|
# ? Mar 8, 2020 00:13 |
|
Either never touch it from C/C++, or access it via a global symbol that you tell the linker to put there
|
# ? Mar 8, 2020 01:17 |
|
Yeah basically just linker magic, it's common to have significant amount of linker tweaks on RTOS/MCU embedded platforms (unix ones not so much). Even more fun is when you have two interrupt vectors for the purpose of firmware flashing, and you have to swap them. e: Or, well, firmware flashing in general is quite fun, since the flash tends to be only writable in pages and there's all sorts of fun to be had there. Especially if you don't have enough space to keep two copies of the program active at once (you write a minimalist flashing routine first, then swap into that, overwrite the main program, and then swap back). Private Speech fucked around with this message at 06:02 on Mar 8, 2020 |
# ? Mar 8, 2020 05:58 |
|
This was a good post. I'll also add that the worst case failure mode of raise(SIGSEGV) is that it does nothing. The worst case of explicitly dereferencing a null pointer is that it rewrites half your code to remove every branch that can reach it, and then compiles that. I am quite partial to this https://godbolt.org/z/P3qvPN as an example of how the optimizer can use "UB is impossible" reasoning to completely mangle your code.
|
# ? Mar 8, 2020 07:14 |
|
This has yielded lots of interesting discussion, thanks guys. The original code was, best we can tell, designed to cause a crash if some 3rd party library failed. Original fail mode was to call exit() which we don’t have. The function is poorly documented and the guy is no longer around. Of course.
|
# ? Mar 8, 2020 14:07 |
|
Xerophyte posted:Anyhow, for the purpose of checking that your bespoke diagnostic signal callback works, explicitly calling the signal handler with raise should to my knowledge be as good as forcing an actual invalid memory access. Does this mean that all the signal context is filled in appropriately, like the faulting address and similar? Not that I can figure out what exactly “appropriately” means in this situation, but one curiosity at a time! E: most of my experience with trapping SEGV and friends is working with crash-reporting systems which depend a lot on that context being correct in order to generate useful reports. I once tried to build a memory mapping system that loaded stuff in manually on fault to unmapped areas, but the ergonomics were really bad, as a person who wasn’t 17-year-old me might have imagined after even brief consideration. Subjunctive fucked around with this message at 06:24 on Mar 12, 2020 |
# ? Mar 12, 2020 06:08 |
|
Subjunctive posted:Does this mean that all the signal context is filled in appropriately, like the faulting address and similar? Not that I can figure out what exactly “appropriately” means in this situation, but one curiosity at a time! I cheerfully assumed that it would be filled in but it looks like I am about to be annoyed as someone, specifically me, informs me that windows does in fact do this in some fun way. I assumed most platforms would have raise simply call the kernel's error handling routine so it could proceed exactly as if the CPU had trapped, filling in some reasonable context and allowing raise to be used to test your error handling code. A quick test on windows shows that I was talking out of my rear end. C++ code:
In hindsight I'm not sure why expected otherwise: signals are platform-specific behavior, so I should expect to write platform-specific code. Xerophyte fucked around with this message at 12:41 on Mar 12, 2020 |
# ? Mar 12, 2020 12:38 |
|
Just found a bug in an Unreal (UE4.22.3) program so weird I have to share it.code:
Edit: I should add that the line of code I posted causes nullptr errors in a different class. Specifically, in an instance of a class that is, like ScoreTextPointer, a member variable of the class that the posted code snippet is from. Can anyone guess what was wrong with that line? I only figured it out by reverting then re-adding the commit that introduced the bug a line at a time. LLSix fucked around with this message at 03:12 on Mar 13, 2020 |
# ? Mar 12, 2020 22:26 |
|
LLSix posted:Just found a bug in an Unreal (UE4.22.3) program so weird I have to share it. What is it? ScoreTextPointer is nullptr? That's the only thing that can be null there, as FString should return a valid string regardless of what value you pass it, as it would be quite dumb to say if(score == INT_MAX) return nullptr .
|
# ? Mar 13, 2020 00:49 |
|
Volguus posted:What is it? ScoreTextPointer is nullptr? That's the only thing that can be null there, as FString should return a valid string regardless of what value you pass it, as it would be quite dumb to say if(score == INT_MAX) return nullptr . No, but that is a sane and sensible guess. I should add that the line of code I posted causes nullptr errors in a different class. Specifically, in an instance of a class that is, like ScoreTextPointer, a member variable of the class that the code is in.
|
# ? Mar 13, 2020 03:11 |
|
LLSix posted:No, but that is a sane and sensible guess. Is it also in a different thread? I've seen that happen when an exception is thrown in one thread, starting the whole program getting destroyed, only for another thread to try to still access data that has just been deallocated. Sometimes you have to do dumb null checks just to get to the actual exception.
|
# ? Mar 13, 2020 04:05 |
|
This is perhaps a dumb question, but whats the best way to throw an error with exceptions turned off? I'm writing code that in certain cases I want to fail completely (because it's only reachable due to programmer error) with a helpful debug message (in debug) and also still bomb in a Release build as well. The code I've been using thus far comes from the official D3D11/D3D12 starter projects, and uses __debugbreak(), along with some helpful printing statements. If I only care about Windows, should I just keep using this?
|
# ? Mar 13, 2020 20:11 |
|
Brownie posted:This is perhaps a dumb question, but whats the best way to throw an error with exceptions turned off? I'm writing code that in certain cases I want to fail completely (because it's only reachable due to programmer error) with a helpful debug message (in debug) and also still bomb in a Release build as well. Comedy option setjmp and longjmp
|
# ? Mar 13, 2020 22:39 |
Have a FatalError() function you can call with whatever message. If you end up needing to, you can then have different implementations of that per platform. The most basic would just be calling abort(), maybe write the message to some kind of log file. On Windows you could maybe consider using the MessageBox() function to display something, just be careful about using it if you do things like fullscreen or locking the mouse cursor position. Also be careful if you have threads dong other things. But if you just want your process to die right now, abort() is the way to do it.
|
|
# ? Mar 13, 2020 22:47 |
|
LLSix posted:No, but that is a sane and sensible guess. So I'm not seeing anything UE4-specific (unless SetText() takes an FText instead of an FString and there's some weirdness with implicit conversions happening?) but my instinct for "another member of the same object is getting hosed up" is memory stomp.
|
# ? Mar 14, 2020 04:10 |
|
Isn’t that allocating an FString on the stack, meaning it’s eligible to be destroyed as soon as the method that made it goes out of scope?
|
# ? Mar 14, 2020 15:51 |
|
If it's affecting subsequent runs in the editor it must be doing something to the editor state that persists after the game is stopped, either clobbering it or stale data not being garbage collected properly would be my initial guesses. If there are static variables being used somewhere they shouldn't be I think that could also cause weirdness like that.
|
# ? Mar 14, 2020 16:44 |
|
more falafel please posted:So I'm not seeing anything UE4-specific (unless SetText() takes an FText instead of an FString and there's some weirdness with implicit conversions happening?) but my instinct for "another member of the same object is getting hosed up" is memory stomp. Very well done. This is my best guess. Effectively a type error the compiler didn't catch and was resulting in it stomping all over memory. The fix was code:
|
# ? Mar 15, 2020 02:10 |
|
The documentation says that there's a deprecated FString overload as well as an FText overload, so it could be that the deprecated overload is just broken.
|
# ? Mar 15, 2020 02:27 |
|
This reminds me of the first game I ever worked on, which I believe had the first integration of Havok physics into UE3. We had Havok engineers onsite to do the integration (because I think Epic wouldn't let them have source themselves?) Anyway, the integration was wrought with problems (not Havok's fault, at least not entirely). Eventually we found a bug that was caused by initializing an hkArray with a function that returned bool. Except it wasn't bool, it was UBOOL, UE3's "great" "bool" type, which was typedef'd to explicitly signed int, for some loving reason. So since hkArray had a preallocating constructor that takes a capacity... everything compiles fine, except obviously nothing works because that's nothing.
|
# ? Mar 18, 2020 00:46 |
|
Umm... what? Latest Visual Studio 2019 has an IntelliSense "warning" that this loop may read past the end of the array. This is uh, hmm C++ code:
IntelliSense says this may read past the end of displayModeList: Warning C6385: Reading invalid data from 'displayModeList': the readable size is 'numModes*28' bytes, but '56' bytes may be read. I don't see how that's possible. edit: it looks like it's assuming that i < numModes will always be true Yeah, of course if you assume that the loop condition is always true it'll eventually run past the end of the array, Microsoft! edit 2: LOL @ it suggesting how Microsoft could improve their own API on the first line of that screenshot edit 3: just gonna turn off IntelliSense warnings and call it good, because Doc Block fucked around with this message at 23:58 on Mar 18, 2020 |
# ? Mar 18, 2020 23:39 |
|
I think what's actually going on there is that it's assuming displayModeList can only ever have one element? Because it's complaining that dML[1] might be invalid? It even says 'valid range 0 to 0'. How does it know how many elements dML has? There's probably a way to hint it.
|
# ? Mar 19, 2020 04:46 |
|
Yeah but even if displayModeList only had one element, numModes would be 1 and so the for loop’s test would catch that. It’s assuming that both displayModeList has one element (because ????), and that also the for loop test passes the second time through even though numModes would be 1 and it would actually fail. In reality, displayModeList will have a dozen or more elements (all the currently attached monitor’s supported display modes), but I think even if I could hint this to IntelliSense, it’d still trip over assuming the for loop test always passes.
|
# ? Mar 19, 2020 06:05 |
|
I should point out that this is just an IntelliSense warning; the actual compiler has no complaints, and the code works fine. Just thought it was weird. Another nag warning added to VS 2019 for something that doesn’t need one (like how it complains about their own API using enum instead of enum class LOL)
|
# ? Mar 19, 2020 06:09 |
displayModeList is an array declared and allocated by the calling program. How is it doing that, operator new, malloc, something else?
|
|
# ? Mar 19, 2020 08:26 |
|
It's hilarious if Microsoft's IntelliSense freaks out over the Microsoft API pattern of "call with nullptr to get size, allocate, then call again with pointer and size to get values".
|
# ? Mar 19, 2020 15:04 |
|
Absurd Alhazred posted:It's hilarious if Microsoft's IntelliSense freaks out over the Microsoft API pattern of "call with nullptr to get size, allocate, then call again with pointer and size to get values". I mean, it should, in any sane world. That's an absolutely terrible paradigm.
|
# ? Mar 19, 2020 16:25 |
|
more falafel please posted:I mean, it should, in any sane world. That's an absolutely terrible paradigm. What would be your alternative that is usable and/or wrappable from C all the way up to C#? Should it be returning some kind of COM shared pointer to the data, that the user would then be required to deallocate?
|
# ? Mar 19, 2020 16:33 |
|
nielsm posted:displayModeList is an array declared and allocated by the calling program. How is it doing that, operator new, malloc, something else? Like Absurd Alhazred said, by calling the function that would fill displayModeList with nullptr to find out how big it needs to be, then allocating it with new, then call the function to fill it again: C++ code:
Doc Block fucked around with this message at 18:46 on Mar 19, 2020 |
# ? Mar 19, 2020 18:28 |
|
I feel like there's got to be a better way to hint the checker to let it know what the size of the array is, but I forget how those annotations work.
|
# ? Mar 19, 2020 18:59 |
|
There’s no way to know in advance how big it’s going to be. It’s every resolution supported by the currently attached monitor and GPU. Knowing in advance how big it is won’t matter, since the checker seems to assume the loop test always passes.
|
# ? Mar 19, 2020 19:38 |
Okay no that's just the compiler being insufficiently smart. You make an allocation with operator new[] of a certain number of elements, the array isn't just going to change size for no reason. Although, declare it as displayModeList[] instead of a pointer, maybe that makes it happier? Also no reason to declare it before assignment. I'm also pretty sure operator new does not return nullptr, either it succeeds or it throws std::bad_alloc. Edit: No wait, yeah obviously numModes can change value between the calls, if you somehow manage to plug in a new monitor in the period between them. So yes the code could theoretically be wrong if an unlikely race condition occurs.
|
|
# ? Mar 19, 2020 19:53 |
|
nielsm posted:Okay no that's just the compiler being insufficiently smart. You make an allocation with operator new[] of a certain number of elements, the array isn't just going to change size for no reason. I think that if that does happen, you will get the new value in numModes after the second call, I think, if it's smaller than in the previous call. Also, I would recommend creating an std::vector instead of using a new allocation directly.
|
# ? Mar 19, 2020 20:04 |
|
I'm trying to learn DirectX, and so this is just following some guy's (admittedly terrible) tutorial. The code samples are terrible, with lots of stuff like code:
Making displayModeList a vector instead of directly allocating it, with no other changes, made the IntelliSense warning go away, at least. According to the docs, if the number of supported display modes for the current monitor were to somehow change between the first and second GetDisplayModeList() calls, the second call would fail with a DXGI_ERROR_MORE_DATA error, since it only uses numModes as an out parameter if you pass nullptr as the destination pointer. It won't change numModes if you give it a destination pointer, so it isn't possible for the for loop to overrun displayModeList. Doc Block fucked around with this message at 20:57 on Mar 19, 2020 |
# ? Mar 19, 2020 20:49 |
|
Doc Block posted:I'm trying to learn DirectX, and so this is just following some guy's (admittedly terrible) tutorial. Huh, fair enough.
|
# ? Mar 19, 2020 21:03 |
|
Absurd Alhazred posted:What would be your alternative that is usable and/or wrappable from C all the way up to C#? Should it be returning some kind of COM shared pointer to the data, that the user would then be required to deallocate? You could pass an allocator function that the API impl can call with the right size before filling it in. Could even give it a sane default, I bet!
|
# ? Mar 20, 2020 00:55 |
|
Subjunctive posted:You could pass an allocator function that the API impl can call with the right size before filling it in. Could even give it a sane default, I bet! Last (and only) time I programmed on Windows (almost a quarter century ago), I remember we ran into problems porting our application because memory allocated in a DLL couldn’t be freed elsewhere. Was this API maybe designed with such a restriction in mind?
|
# ? Mar 20, 2020 23:18 |
|
Zopotantor posted:Last (and only) time I programmed on Windows (almost a quarter century ago), I remember we ran into problems porting our application because memory allocated in a DLL couldn’t be freed elsewhere. Was this API maybe designed with such a restriction in mind? You could always go the Vulkan route and have an optional user-side allocation parameter.
|
# ? Mar 20, 2020 23:21 |
|
|
# ? Jun 7, 2024 23:42 |
Zopotantor posted:Last (and only) time I programmed on Windows (almost a quarter century ago), I remember we ran into problems porting our application because memory allocated in a DLL couldn’t be freed elsewhere. Was this API maybe designed with such a restriction in mind? Yeah, Windows has always operated with the assumption that every program and shared library has its own copy of the C library. Two binaries in the same process won't necessarily be able to free memory allocated on the other side. So, if you want to function on Windows you need to either have callers allocate all memory, have callers supply an allocator interface, or export a free() function callers can use on objects you allocate for them. (This is also why it's always puzzled me that Mingw went the route of linking in Microsoft's C runtime instead of using one of their own. Every other compiler and language on Windows does that, only Mingw decided the painful route of linking someone else's proprietary library.) nielsm fucked around with this message at 23:36 on Mar 20, 2020 |
|
# ? Mar 20, 2020 23:34 |