flat assembler
Message board for the users of flat assembler.

Index > Windows > handling errors by exception

Author
Thread Post new topic Reply to topic
b1528932



Joined: 21 May 2010
Posts: 287
b1528932
I dont know if only i have this 'problem', but i have idea how do get rid of it.
When i write a windows code wich use much of imported functions wich return a value, i have to check every them single time. I think you can imagine how my code looks like after 5 functions.

Code:
WaitForSingle)boect_fail:
;do cleanup and choose next action depending on GLA
PostQueuedCompletionStatus_fail:
;do cleanup and choose next action depending on GLA
CreateFileW_fail:
    

And so on.

Each time i call a function, i have to prepare to handle multiple conditions (because some errors might be fatal (EINVAL), some not (ERROR_FILE_NOT_FOUND, ERROR_ACCESS_DENIED), and some expected (WSAEWOUDBLOCK, ERROR_IO_PENDING). Some are mysterious, because they might occur during normal execution, and i have no idea how to handle them (ERROR_PARTIAL_COPY, EINPROGRESS, not enough memory)
).

Some functions might even return success, and contain additional condition wich i propably be aware of (CreateFile).


I was thinking, is it a good idea instead of doing tons of conditional jumps and compares use RaiseException() instead. I install SEH, and if an error occur, i will be able to handle it.
It might be less efficient when actual error will occur, but code propably would look much better. I could define a set of generic paths or example handling fatal exception (eg. invalid parameter, wich will only happen in case of memory corruption or tampering). I would also prepare specyfic paths. I would to test api for 2 basic conditions (success/fail), and in case of fail, pass GetLastError as an argument of exception and unique value as code.

Ive seen this in some software, thats why im considering it.

Is it a good idea, or not?
'exception' or jz _specyfic_error_handler? whats better and why?[/code]
Post 14 Feb 2011, 09:25
View user's profile Send private message Reply with quote
revolution
When all else fails, read the source


Joined: 24 Aug 2004
Posts: 17450
Location: In your JS exploiting you and your system
revolution
I think anything generic would not have enough flexibility.

Besides you are just moving the problem of error handling to some other place, you are not eliminating your "problem".
Post 14 Feb 2011, 09:44
View user's profile Send private message Visit poster's website Reply with quote
vid
Verbosity in development


Joined: 05 Sep 2003
Posts: 7105
Location: Slovakia
vid
Yep, this method is used for error handling. You write a wrapper for every function call, which checks for returned error code and raises exception based on it. Then in rest of code, you only call your wrappers, never the API directly.

But, if you really want proper cleanup, this might not be worth of effort. For example if you do something like this pseudo-code:
Code:
call CreateFile
cmp eax, -1
je error
...
call WriteFile
cmp eax, 0
je error2
...
error2:
call CloseFile, ...  ;cleanup opened file handle
error:
ret    

With error code returned directly, this is easy. But with exceptions, you would have to set up different handler after successful CreateFile, one that releases the handle. In real OOP this is solved by compiler automatically calling destructor. In Asm, you have nothing of sorts. You'd have to set up different handler after each call that allocates something what mu be freed, and that might turn out being more troubling than error checking after each API call.

For WinAPIs, I personally prefer to error create macro that takes formatted (printf-like) error message, appends string representation of last error (GetLastError + FormatMessage), and displays it. It looks like:
Code:
...
push [filename]
call CreateFile
cmp eax, -1
jne @f
winerr "Failed to open file '%s'", [filename]
mov eax, <MY_ERROR_CODE>
jmp <return_from_proc>
@@:
    

Upon error, displayed message might look like: Failed to open file 'foobar.txt': The system cannot find the file specified.

This way you have all of API-call-related code in one place (no CreateFile_error label somewhere else in code), even though it takes few extra lines. To save few more lines, you can wrap common error code tests to macro, like:
Code:
; check for error returned as 0 in eax
macro winerr0 errcode, errlabel, [args] {
common
local ..end
cmp eax, 0
jne ..end
winerr args
mov eax, errcode
jmp errlabel
..end:
}

...

call CreateFile
winerr0 MY_ERROR_CODE, return_label, "Failed to open file '%s'", [filename]
    
Post 14 Feb 2011, 10:30
View user's profile Send private message Visit poster's website AIM Address MSN Messenger ICQ Number Reply with quote
vid
Verbosity in development


Joined: 05 Sep 2003
Posts: 7105
Location: Slovakia
vid
Also note that most of the time when API returns error, you simply want to clean up and return error. Cases where you want to handle one specific error code differently are quite rare.
Post 14 Feb 2011, 10:35
View user's profile Send private message Visit poster's website AIM Address MSN Messenger ICQ Number Reply with quote
b1528932



Joined: 21 May 2010
Posts: 287
b1528932
Quote:
Cases where you want to handle one specific error code differently are quite rare.

When doing overlapped io, i always get error. When the other side is closed (pipe), i get diffrent error.


Idea of constructor/destructor is ok, but then how to make it. I could do it 'per api', 'per operation', or 'per program'. How do i chose best scale.

For example, i want to write data to file. Should i setup constructor/destructor for entire operation, or separatly for CreateFile/CreateIoCompletionPort/PostQueuedCompletionStatus/GetQueuedCompletionStatus/LockFileEx/WriteFile/UnlockFileEx/CloseHandle. How does HLL do it? I recall a term 'class', wich i belive it is what hold those things. Does HLL class (should) handle entire write, or should be wrapped around each api call? I have 0 (literaly, ZERO) experience in HLL, i got stuck multiple times in past inside c problems, and decided that its not for me.



Ok i have a question.
What if inside my error handler error occur? Note that FormatMessage do not return void. What do i do then? Its just an example, CloseHandle is other one. I always wonder what shgould i do in my error handler when it produce error. I could kill entire program, but exception would be better because if someone is debugging, he could recover. I could just ignore it, and proceed with next part of handler. But then why not ignore error in 'real' program, i cant (or can i?) decide that this code is unimportant and i dont care if error occured. What if CloseHandle fail? I have no solution to this day, i just do conditional jump and return without doing anything.
Why functions like CloseHandle (destructors of objects) have even a possibility of failure? I can ensure that object is valid, its illegal to any program interfere with my and tamper with my handles, so i have 100% control of that handle if other programs folows documentation. The only reason closeHandle can fail is HANDLE_FLAG_PROTECT_FROM_CLOSE.
Why would they create such thing? And even if they did, calling CloseHandle should be void, if handle cant be closed because of HANDLE_FLAG_PROTECT_FROM_CLOSE, ok it wont but its user dont have to know that handle is still alive. He closed, and can wash his hands from memleak possibility.
This option can cause BSoD in XP btw, local DoS.
Post 14 Feb 2011, 11:45
View user's profile Send private message Reply with quote
vid
Verbosity in development


Joined: 05 Sep 2003
Posts: 7105
Location: Slovakia
vid
Quote:
Quote:
Cases where you want to handle one specific error code differently are quite rare.
When doing overlapped io, i always get error. When the other side is closed (pipe), i get diffrent error.

Of course I meant rare coding-wise, not runtime-wise. Writing extra error code checking here and there doesn't hurt.

Quote:
Idea of constructor/destructor is ok, but then how to make it.

There is no normal way in Asm, without really heavy macroing. That's what we have object-oriented HLL for.

Quote:
What if inside my error handler error occur? Note that FormatMessage do not return void. What do i do then?

If FormatMessage returns error (it does happen!), I only print numeric representation of error code, eg Failed to open 'a.txt': <Windows error 256 (0x100)>.

Quote:
Its just an example, CloseHandle is other one.

If CloseHandle inside error handler fails, there is not much to do, aside from letting programmer know it failed (CloseHandle failing means programmer error). So, theoretical ideal would be to print error in Debug build, and simply continue in Release build. In reality however, everyone ignores return code from CloseHandle. Programming mistakes which could be discovered by checking return value of CloseHandle are quite rare, so no one bothers.

Quote:
I always wonder what shgould i do in my error handler when it produce error.

There is no universal recipe. I'd say you should try to do as much as possible (display error info, clean up), and then return to caller. Why? Some errors are recoverable, some are not. Sometimes you want to recover from error, sometimes you don't. Most often, when writing some inner routine, you don't know if one who calls this routine would want to recover or not.

So again, my best "general" advice is to cleanup as much as possible upon error, return (original) error status to caller, and let him decide what to do. Caller can handle error somehow if he wishes, or easily "pass" the error to it its own caller. If unhandled error reaches "topmost" level, simply exit your app. Question at what level to display error really can't be answered generally, you must decide in each case.

Note that this requires every procedure to be able to signal error, and error to be checked at every call. In Asm-only projects without too many callbacks, CF is great for this. More "standard" way that can be mixed with HLLs is for every procedure to return error code in EAX (0 means okay, nonzero means error code). This way, passing error from called procedure to caller takes only two extra instructions per call. Looks like this:
Code:
MyProc proc

  ;returns error code in eax
  call SomeFunc

  ;if there was some error, pass it to caller
  cmp eax, 0
  jnz r

  call SomeFunc2
  ;handle one kind of error
  cmp eax, ERROR_SOMETHING
  je handler_for_something
  ;pass other errors
  cmp eax, 0
  jnz r

  ;success
  xor eax, eax
r:
  ret

handler_for_something:
  ...
endp    
Post 14 Feb 2011, 12:41
View user's profile Send private message Visit poster's website AIM Address MSN Messenger ICQ Number Reply with quote
Display posts from previous:
Post new topic Reply to topic

Jump to:  


< Last Thread | Next Thread >
Forum Rules:
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum
You cannot attach files in this forum
You can download files in this forum


Copyright © 1999-2020, Tomasz Grysztar. Also on YouTube, Twitter.

Website powered by rwasa.