flat assembler
Message board for the users of flat assembler.
 Home   FAQ   Search   Register 
 Profile   Log in to check your private messages   Log in 
flat assembler > Main > Alignment and padding

Goto page 1, 2  Next
Author
Thread Post new topic Reply to topic
VEG



Joined: 06 Feb 2013
Posts: 50
Location: Minsk, Belarus

Code:
PaddingInRSRC   |       zero    |       nop(90h)|       that is good


Actually, using nop for padding is not a very good decision. 0xCC prevents these bytes from accidental execution and breaks execution instantly (0xCC = int 3). nop just doesn't make any sense if these bytes aren't planned to be executed.

I had a situation when I had forgot to write a retn in a procedure. And the program worked at first sight Smile There were several nops for alignment and the next function which did something useless which didn't cause crashes in this situation. This problem was unnoticed for some time. And then I've seen that Clang + LLD use 0xCC as a filler. I have liked this idea. Now I think that 0xCC is better than 0x90. It even has some mnemonic sense like "Clear Clear" =) A good variant for marking something that it has not to be executed.
Post 30 Apr 2017, 15:53
View user's profile Send private message Visit poster's website Reply with quote
Furs



Joined: 04 Mar 2016
Posts: 290
I agree, btw it's one of the complaints I had with GNU GAS (i.e. basically stuff compiled with GCC) but as usual they don't give a shit of good suggestions, which is one of the reasons I dislike the FSF because of their attitude. Yes, GCC/GAS pads with nops non-accessible sections of code, sometimes multi-byte nops. That's retarded.

Though, any unprivileged 1-byte instruction does the job (hlt comes to mind) not just int3.
Post 30 Apr 2017, 20:01
View user's profile Send private message Reply with quote
system error



Joined: 01 Sep 2013
Posts: 471

VEG wrote:

Code:
PaddingInRSRC   |       zero    |       nop(90h)|       that is good


Actually, using nop for padding is not a very good decision. 0xCC prevents these bytes from accidental execution and breaks execution instantly (0xCC = int 3). nop just doesn't make any sense if these bytes aren't planned to be executed.

I had a situation when I had forgot to write a retn in a procedure. And the program worked at first sight Smile There were several nops for alignment and the next function which did something useless which didn't cause crashes in this situation. This problem was unnoticed for some time. And then I've seen that Clang + LLD use 0xCC as a filler. I have liked this idea. Now I think that 0xCC is better than 0x90. It even has some mnemonic sense like "Clear Clear" =) A good variant for marking something that it has not to be executed.



0xCC is actually not used for paddings. It's microsoft's own security thingies. (security patches, security certificates etc), most probably to prevent buffer overrun among other evil things.

On the contrary, 0xCC means do something, like invoking a debugger. NOP simply means do nothing. If you're using PowerPoint for presentation before the world audience, you don't want a 0xCC to 'interrupt' your presentations by invoking CPU's internal porn if something went wrong. It's ugly and disastrous from HCI standpoint.
Post 01 May 2017, 00:35
View user's profile Send private message Reply with quote
revolution
When all else fails, read the source


Joined: 24 Aug 2004
Posts: 14672
Location: Origae-6

system error wrote:
If you're using PowerPoint for presentation before the world audience, you don't want a 0xCC to 'interrupt' your presentations ...

I agree, no one wants this. But also no one wants nops being executed and followed by some unexpected code path doing unexpected things.

I think I'd rather have int3 being executed and crashing the program. At least then I'd immediately know it is faulty and where is faulted. The alternative with nops is potentially a much harder problem to discover and fix.
Post 01 May 2017, 00:41
View user's profile Send private message Visit poster's website Reply with quote
system error



Joined: 01 Sep 2013
Posts: 471

revolution wrote:

system error wrote:
If you're using PowerPoint for presentation before the world audience, you don't want a 0xCC to 'interrupt' your presentations ...

I agree, no one wants this. But also no one wants nops being executed and followed by some unexpected code path doing unexpected things.

I think I'd rather have int3 being executed and crashing the program. At least then I'd immediately know it is faulty and where is faulted. The alternative with nops is potentially a much harder problem to discover and fix.



Both crashes / freezes the same way anyway. But that extra 'capability' of int3 to invoke something else is the reason why it should not be used for user application paddings, let alone alignment padding. NOP is lightweight. int3 is an overkill for purposes other than debugging and developing. Btw, in many areas, Windows do use NOPs for padding purposes but use 0xCC where security measures are needed to immediately redirect suspicious codes to the internal debugger.
Post 01 May 2017, 00:51
View user's profile Send private message Reply with quote
revolution
When all else fails, read the source


Joined: 24 Aug 2004
Posts: 14672
Location: Origae-6

Code:
;...
proc animate_rainbows_and_unicorns ...
  push ebp
  ;exciting animation code goes here
  pop ebp ;we're done
endp ;oops forgot the ret
nop ;alignment
proc delete_all_user_data_and_DBs ...
  push ebp
  ;deletion code goes here
;...

Hmm, I think int3 might be a better option there.
Post 01 May 2017, 00:58
View user's profile Send private message Visit poster's website Reply with quote
system error



Joined: 01 Sep 2013
Posts: 471

revolution wrote:

Code:
;...
proc animate_rainbows_and_unicorns ...
  push ebp
  ;exciting animation code goes here
  pop ebp ;we're done
endp ;oops forgot the ret
nop ;alignment
proc delete_all_user_data_and_DBs ...
  push ebp
  ;deletion code goes here
;...

Hmm, I think int3 might be a better option there.



I don't think so. int3 is never a solution and never design to be a solution to programmers own mistakes in their final products. We have flowcharts, pseudocodes, ERD diagrams etc for that. You don't sell 'debuggish' products to end-users. Similarly in ARM/Android, I don't want to see the content of the registers once the CandyCrush crashed, let alone 10 year olds and grannies!
Post 01 May 2017, 01:08
View user's profile Send private message Reply with quote
system error



Joined: 01 Sep 2013
Posts: 471
Here's some light reading on how Windows uses 0xCC as a security measure - not exactly for general padding 'solution'.

http://www.ksyash.com/2011/01/buffer-overflow-protection-3/
Post 01 May 2017, 01:26
View user's profile Send private message Reply with quote
VEG



Joined: 06 Feb 2013
Posts: 50
Location: Minsk, Belarus

Quote:
Both crashes / freezes the same way anyway.

Not always. Sometimes it can execute some code which will not cause a crash directly. I had such situation. 0xCC as a filler could help.

Quote:
But that extra 'capability' of int3 to invoke something else is the reason why it should not be used for user application paddings

No. It is the reason why it is better to use 0xCC instead of 0x90. Just because 0x90 just silently executes and doing nothing, and 0xCC just prevents from execution something that has not to be executed. 0xCC just marks all paddings like "it is clear, nothing to execute here, go away!".


Quote:
NOP is lightweight.

0x90 as lightweight as 0xCC. Just one byte in both situations.


Quote:
0xCC is actually not used for paddings. It's microsoft's own security thingies.

You're not right. 0xCC often is used for paddings. As I had mentioned it before, LLVM uses 0xCC for paddings in x86 programs. LLVM is used for compiling C++ or Rust programs, as well as many other different languages. It seems that Microsoft Visual C++ also uses 0xCC for paddings between procedures:
Image

IMHO, three types of paddings are good in different situations:
— nop (0x90 and 2+ bytes variants) is good for padding things inside a procedure, just because it is intended to be executed.
— 0xCC is good for padding between procedures, just because this code isn't intended to be executed Smile
— 0x00 for padding in data sections.
Post 01 May 2017, 04:58
View user's profile Send private message Visit poster's website Reply with quote
revolution
When all else fails, read the source


Joined: 24 Aug 2004
Posts: 14672
Location: Origae-6
- 0xFF for padding in FLASH & EEPROM memories.
Post 01 May 2017, 05:45
View user's profile Send private message Visit poster's website Reply with quote
revolution
When all else fails, read the source


Joined: 24 Aug 2004
Posts: 14672
Location: Origae-6

system error wrote:
I don't think so. int3 is never a solution and never design to be a solution to programmers own mistakes in their final products. We have flowcharts, pseudocodes, ERD diagrams etc for that. You don't sell 'debuggish' products to end-users. Similarly in ARM/Android, I don't want to see the content of the registers once the CandyCrush crashed, let alone 10 year olds and grannies!

A "real" application should catch all exceptions and either kill itself or present some kind of "we are sorry" dialog. And computer systems are not perfect anyway so hardware errors can also create havoc so you should've been catching exceptions anyway regardless of how well debugged the code is.

I can't agree that in my code above that nop is a better choice. I also can't agree about measuring the weight (lightweight solution?) of an instruction that isn't supposed to be executed.

There is the recommendation in the AMD manual that suggests to use int3 for padding to tell the prefetcher & decoder to stop wasting power and resources and do something else.
Post 01 May 2017, 05:58
View user's profile Send private message Visit poster's website Reply with quote
zhak



Joined: 12 Apr 2005
Posts: 468
Location: Belarus
Why aligning with zeros is not considered? Paddings are not intended to be executed. If padded area is executed, it is an error and will lead to a crash sooner or later. So why not just align with zeros both code and data?
Post 01 May 2017, 09:22
View user's profile Send private message Reply with quote
Furs



Joined: 04 Mar 2016
Posts: 290

zhak wrote:
Why aligning with zeros is not considered? Paddings are not intended to be executed. If padded area is executed, it is an error and will lead to a crash sooner or later. So why not just align with zeros both code and data?

What's the point to zero pad when you can use int3 or other 1-byte instruction that will fault?

Zero pad could lead to patterns being recognized as completely different instructions. Maybe unlikely, but still possible, so why do it when it offers zero advantages to int3? Confused


system error wrote:
I don't think so. int3 is never a solution and never design to be a solution to programmers own mistakes in their final products. We have flowcharts, pseudocodes, ERD diagrams etc for that. You don't sell 'debuggish' products to end-users. Similarly in ARM/Android, I don't want to see the content of the registers once the CandyCrush crashed, let alone 10 year olds and grannies!

Crash Dumps aren't made to be read by the casual user, they're simply made to be submitted (if they want to) to the support team, which will forward it to the developers in question so they can track the bug.

Even a grandma can copy paste and submit a crash dump if she wants to.


Also: "security" exploits happen because of badly written code. Perfectly written code has no exploits.

Furthermore, 0xCC can help with more than just "exploits" and "badly written code". Suppose there's a fault in the CPU, or a bit flip in the CPU cache's itself due to quantum effects or cosmic rays or whatever else you can't control, and somehow it changed the instruction pointer's bit so it now points to padding. (perhaps it changed the return address on the stack)

Would you rather have the application crash so there's no room for disaster or let it continue and silently ruin the rest of your data? (because you have no idea such a flip even happened). Note that it is unlikely it won't also crash with nops, because it will most likely (99.9999%) crash also with nops, so what's the difference in this case? Except making it harder to track down (because it will crash at a completely innocent-looking instruction, due to messed up stack or whatever).

Even so, revolution's example demonstrates what can happen if it will NOT crash (the 0.0001% or whatever); it could even delete the entire database. Extremely unlikely, but still a chance higher than zero, and the alternative (crash) is absolutely not any better than 0xCC, so what's the point?!?

It's unlikely but still higher than zero chance -- and there's no disadvantage to do it, so why not?

The application should rather crash than continue executing with the wrong instructions. The same reason your PC will automatically crash by design if an ECC error is detected in RAM: you seriously do not want to continue with corrupted memory which can do unspeakable things.

Of course with ECC RAM you will need two bit flips (highly unlikely) for it to crash by design, since it can automatically correct 1 bit errors. That's not the reason I use ECC RAM though. Double bit flips are incredibly rare, but non-ECC RAM cannot detect them as far as I know.

That leads to silent corruption of data. Silent because your computer won't crash (just as you want) and you'll have no idea it even happened, until you save the file, back it up, then years later try to reload it and find out it's fucked up. Backup won't do you any good if the file's data itself is messed up before it is even written to the backup.
Post 01 May 2017, 10:38
View user's profile Send private message Reply with quote
system error



Joined: 01 Sep 2013
Posts: 471

VEG wrote:
Not always. Sometimes it can execute some code which will not cause a crash directly. I had such situation. 0xCC as a filler could help.



That's exactly what I meant by 'possibility'. No further work needed for NOP.


Quote:
0x90 as lightweight as 0xCC. Just one byte in both situations.



0xCC is not a lightweight instruction. Don't be fooled by '1' byte convenience. It involves myriads of microcodes and CPU resources. You can't compare that with 'do nothing' 1-byte NOP. I don't understand how you defined lightweight vs heavyweight.


Quote:
You're not right. 0xCC often is used for paddings. As I had mentioned it before, LLVM uses 0xCC for paddings in x86 programs. LLVM is used for compiling C++ or Rust programs, as well as many other different languages. It seems that Microsoft Visual C++ also uses 0xCC for paddings between procedures:

IMHO, three types of paddings are good in different situations:
— nop (0x90 and 2+ bytes variants) is good for padding things inside a procedure, just because it is intended to be executed.
— 0xCC is good for padding between procedures, just because this code isn't intended to be executed Smile
— 0x00 for padding in data sections.



It is not padding in general sense. Padding is applied at compile time.
int3 is a security measure and counter-measure which is activated at runtime like for example providing guard bytes for local variables (means stack that grows and shrinks dynamically). Also for security cookies. Do you write your own security cookies for your programs? Nope. Because it's generated at runtime. It's not convenient if not practically impossible. Maybe we can ask Tomasz Gryzstar to provide PROC and INVOKE macros that has 0xCC at each ends and at every locals? How convenient could that be?

int3 is a high-level language thing which is implemented with full support from the OS, like VC++ or else you'll have to write your own interrupt handlers for every different part of your codes.
Post 02 May 2017, 02:29
View user's profile Send private message Reply with quote
system error



Joined: 01 Sep 2013
Posts: 471

revolution wrote:

system error wrote:
I don't think so. int3 is never a solution and never design to be a solution to programmers own mistakes in their final products. We have flowcharts, pseudocodes, ERD diagrams etc for that. You don't sell 'debuggish' products to end-users. Similarly in ARM/Android, I don't want to see the content of the registers once the CandyCrush crashed, let alone 10 year olds and grannies!

A "real" application should catch all exceptions and either kill itself or present some kind of "we are sorry" dialog. And computer systems are not perfect anyway so hardware errors can also create havoc so you should've been catching exceptions anyway regardless of how well debugged the code is.

I can't agree that in my code above that nop is a better choice. I also can't agree about measuring the weight (lightweight solution?) of an instruction that isn't supposed to be executed.

There is the recommendation in the AMD manual that suggests to use int3 for padding to tell the prefetcher & decoder to stop wasting power and resources and do something else.



Catching exceptions should be done by the O.S and by the internal code even more conveniently and safely than to let the debugger catch it and blow things up in your face.

Try Java that runs 'billions of devices' without having to expose it's own internal bytecodes everytime they screwed up half-way. The principle is simple: let software faults be handled by software exception handlers. Using 0xCC to trigger some hardware reactions might not be safe anyway if compared to plain 0x90.
Post 02 May 2017, 02:41
View user's profile Send private message Reply with quote
system error



Joined: 01 Sep 2013
Posts: 471

Quote:
-- snipped --
Would you rather have the application crash so there's no room for disaster or let it continue and silently ruin the rest of your data? (because you have no idea such a flip even happened). Note that it is unlikely it won't also crash with nops, because it will most likely (99.9999%) crash also with nops, so what's the difference in this case? Except making it harder to track down (because it will crash at a completely innocent-looking instruction, due to messed up stack or whatever).

Even so, revolution's example demonstrates what can happen if it will NOT crash (the 0.0001% or whatever); it could even delete the entire database. Extremely unlikely, but still a chance higher than zero, and the alternative (crash) is absolutely not any better than 0xCC, so what's the point?!?

It's unlikely but still higher than zero chance -- and there's no disadvantage to do it, so why not?

The application should rather crash than continue executing with the wrong instructions. The same reason your PC will automatically crash by design if an ECC error is detected in RAM: you seriously do not want to continue with corrupted memory which can do unspeakable things.

Of course with ECC RAM you will need two bit flips (highly unlikely) for it to crash by design, since it can automatically correct 1 bit errors. That's not the reason I use ECC RAM though. Double bit flips are incredibly rare, but non-ECC RAM cannot detect them as far as I know.

That leads to silent corruption of data. Silent because your computer won't crash (just as you want) and you'll have no idea it even happened, until you save the file, back it up, then years later try to reload it and find out it's fucked up. Backup won't do you any good if the file's data itself is messed up before it is even written to the backup.



If ECC error occurs, then it's called malfunction PC or mobo. Hahaha. It has nothing to do with 0x90. Even 0xCC won't work by that time. Also don't forget the facts that a mishandled hardware interrupt can ruin your entire harddisk too! Just so you know that int3 is just a 'protected' int 13h. They are from the same interrupt family by the way Very Happy
Post 02 May 2017, 02:56
View user's profile Send private message Reply with quote
Furs



Joined: 04 Mar 2016
Posts: 290
ECC has nothing to do with 0x90, no, but read the context again. The context was about problems out of your control. You know, random bit flips can happen, right? Go look it up on revolution's website. It's batshit crazy in my opinion to not use ECC memory these days because of the densities of RAM cells are so high (thus easily flipped). Even the damn GPU RAM uses ECC. It's not always faulty hardware. ECC protects against random bit flips. Cosmic ray radiation exists (in a plane there's a higher chance of a bit flip since you're not as protected as at sea level by the Earth) and other factors (such as quantum tunneling) which can randomly flip a bit.

However, let's go back to the point. How do you think exceptions get generated? int3 triggers an exception, so does hlt or other unprivileged instruction. nop doesn't, and that's the problem.

Do you understand how stupidly unsafe it is to run an arbitrary function? The return address of the function is pushed on the stack, but if you run a random function (because of nops) instead of triggering an exception, it will "return" to whatever was on top of the stack before.

This return can be any arbitrary code (to delete your data), but it most likely will just crash because it will be an invalid page, or it will point to data instead of code, or whatever.

So there's 2 cases:

Case 1: nop crashes just as 0xCC does with an exception -- except it makes tracking it down way harder, so why use it?
Case 2: nop doesn't crash but does something horrible executing random code (perhaps an attacker already exploited it!)

Tell me in which case does nop even hold an advantage?

If anything it's a hazard because of case 2, and an annoyance because of case 1 (hard to track down, it will crash at a random/innocent instruction due to bad stack etc).


But it seems you think int3 automatically runs the debugger and it has magical powers instead of just triggering an exception (that can be identified by the debugger of course). So use another unprivileged instruction then, like hlt. hlt will trigger an exception since it's not a useable instruction from usercode so you can let Windows do its magical thing you think it does. (it's also 1 byte long so it's safe)

No excuse to use nop.
Post 02 May 2017, 12:17
View user's profile Send private message Reply with quote
system error



Joined: 01 Sep 2013
Posts: 471

Furs wrote:
No excuse to use nop.

You mean, just because someone overrun the buffer by one byte, he has to deal with this 1-byte "convenience", invoking Trap Gate, Task Gate, setting up IVT, entering etc etc?


Code:
IF PE = 0
    THEN
         GOTO REAL-ADDRESS-MODE;
    ELSE (* PE = 1 *)
         IF (VM = 1 and IOPL < 3 AND INT n)
              THEN
                     #GP(0); (* Bit 0 of error code is 0 because INT n *)
              ELSE (* Protected modeIA-32e modeor virtual-8086 mode interrupt *)
                    IF (IA32_EFER.LMA = 0)
                         THEN (* Protected modeor virtual-8086 mode interrupt *)
                              GOTO PROTECTED-MODE;
                    ELSE (* IA-32e mode interrupt *)
                         GOTO IA-32e-MODE;
                    FI;
         FI;
FI;
REAL-ADDRESS-MODE:
    IF ((vector_number « 2) + 3is not within IDT limit
         THEN #GP; FI;
    IF stack not large enough for a 6-byte return information
         THEN #SS; FI;
    Push (EFLAGS[15:0]);
    IF  0; (* Clear interrupt flag *)
    TF  0; (* Clear trap flag *)
    AC  0; (* Clear AC flag *)
    Push(CS);
    Push(IP);
    (* No error codes are pushed in real-address mode*)
    CS  IDT(Descriptor (vector_number « 2), selector));
    EIP  IDT(Descriptor (vector_number « 2), offset)); (* 16 bit offset AND 0000FFFFH *)
END;
PROTECTED-MODE:
    IF ((vector_number « 3) + 7is not within IDT limits
    or selected IDT descriptor is not an interrupt-, trap-, or task-gate type
         THEN #GP(error_code(vector_number,1,EXT)); FI;
         (* idt operand to error_code set because vector is used *)
    IF software interrupt (* Generated by INT nINT3or INTO *)
         THEN
              IF gate DPL < CPL (* PE = 1DPL < CPLsoftware interrupt *)
                    THEN #GP(error_code(vector_number,1,0)); FI;
                    (* idt operand to error_code set because vector is used *)
                    (* ext operand to error_code is 0 because INT nINT3or INTO*)
    FI;
    IF gate not present
         THEN #NP(error_code(vector_number,1,EXT)); FI;
         (* idt operand to error_code set because vector is used *)
    IF task gate (* Specified in the selected interrupt table descriptor *)
         THEN GOTO TASK-GATE;
         ELSE GOTO TRAP-OR-INTERRUPT-GATE; (* PE = 1, trap/interrupt gate *)
    FI;
END;
IA-32e-MODE:
    IF INTO and CS.L = 1 (64-bit mode)
         THEN #UD;
    FI;
    IF ((vector_number « 4) + 15is not in IDT limits
    or selected IDT descriptor is not an interrupt-, or trap-gate type
         THEN #GP(error_code(vector_number,1,EXT));
         (* idt operand to error_code set because vector is used *)
    FI;
    IF software interrupt (* Generated by INT nINT 3or INTO *)
         THEN
              IF gate DPL < CPL (* PE = 1DPL < CPLsoftware interrupt *)
                    THEN #GP(error_code(vector_number,1,0));
                    (* idt operand to error_code set because vector is used *)
                    (* ext operand to error_code is 0 because INT nINT3or INTO*)
              FI;
    FI;
    IF gate not present
         THEN #NP(error_code(vector_number,1,EXT));
         (* idt operand to error_code set because vector is used *)
    FI;
    GOTO TRAP-OR-INTERRUPT-GATE; (* Trap/interrupt gate *)
END;
TASK-GATE: (* PE = 1task gate *)
    Read TSS selector in task gate (IDT descriptor);
         IF local/global bit is set to local or index not within GDT limits
              THEN #GP(error_code(TSS selector,0,EXT)); FI;
              (* idt operand to error_code is 0 because selector is used *)
         Access TSS descriptor in GDT;
         IF TSS descriptor specifies that the TSS is busy (low-order 5 bits set to 00001)
              THEN #GP(TSS selector,0,EXT)); FI;
              (* idt operand to error_code is 0 because selector is used *)
         IF TSS not present
              THEN #NP(TSS selector,0,EXT)); FI;
              (* idt operand to error_code is 0 because selector is used *)
    SWITCH-TASKS (with nestingto TSS;
    IF interrupt caused by fault with error code
         THEN
              IF stack limit does not allow push of error code
                    THEN #SS(EXT); FI;
              Push(error code);
    FI;
    IF EIP not within code segment limit
         THEN #GP(EXT); FI;
END;
TRAP-OR-INTERRUPT-GATE:
    Read new code-segment selector for trap or interrupt gate (IDT descriptor);
    IF new code-segment selector is NULL
         THEN #GP(EXT); FI; (* Error code contains NULL selector *)
    IF new code-segment selector is not within its descriptor table limits
         THEN #GP(error_code(new code-segment selector,0,EXT)); FI;
         (* idt operand to error_code is 0 because selector is used *)
    Read descriptor referenced by new code-segment selector;
    IF descriptor does not indicate a code segment or new code-segment DPL > CPL
         THEN #GP(error_code(new code-segment selector,0,EXT)); FI;
         (* idt operand to error_code is 0 because selector is used *)
    IF new code-segment descriptor is not present,
         THEN #NP(error_code(new code-segment selector,0,EXT)); FI;
         (* idt operand to error_code is 0 because selector is used *)
    IF new code segment is non-conforming with DPL < CPL
         THEN
              IF VM = 0
                    THEN
                         GOTO INTER-PRIVILEGE-LEVEL-INTERRUPT;
                         (* PE = 1VM = 0interrupt or trap gatenonconforming code segment,
                         DPL < CPL *)
                    ELSE (* VM = 1 *)
                         IF new code-segment DPL  0
                              THEN #GP(error_code(new code-segment selector,0,EXT));
                              (* idt operand to error_code is 0 because selector is used *)
                         GOTO INTERRUPT-FROM-VIRTUAL-8086-MODE; FI;
                         (* PE = 1interrupt or trap gateDPL < CPLVM = 1 *)
              FI;
         ELSE (* PE = 1interrupt or trap gateDPL  CPL *)
              IF VM = 1
                    THEN #GP(error_code(new code-segment selector,0,EXT));
                    (* idt operand to error_code is 0 because selector is used *)
              IF new code segment is conforming or new code-segment DPL = CPL
                    THEN
                         GOTO INTRA-PRIVILEGE-LEVEL-INTERRUPT;
                    ELSE (* PE = 1interrupt or trap gatenonconforming code segmentDPL > CPL *)
                         #GP(error_code(new code-segment selector,0,EXT));
                         (* idt operand to error_code is 0 because selector is used *)
              FI;
    FI;
END;
INTER-PRIVILEGE-LEVEL-INTERRUPT:
    (* PE = 1interrupt or trap gatenon-conforming code segmentDPL < CPL *)
    IF (IA32_EFER.LMA = 0) (* Not IA-32e mode *)
         THEN
         (* Identify stack-segment selector for new privilege level in current TSS *)
              IF current TSS is 32-bit
                    THEN
                         TSSstackAddress  (new code-segment DPL « 3) + 4;
                         IF (TSSstackAddress + 5) > current TSS limit
                              THEN #TS(error_code(current TSS selector,0,EXT)); FI;
                              (* idt operand to error_code is 0 because selector is used *)
                         NewSS  2 bytes loaded from (TSS base + TSSstackAddress + 4);
                         NewESP  4 bytes loaded from (TSS base + TSSstackAddress);
                    ELSE
                              (* current TSS is 16-bit *)
                         TSSstackAddress  (new code-segment DPL « 2) + 2
                         IF (TSSstackAddress + 3) > current TSS limit
                              THEN #TS(error_code(current TSS selector,0,EXT)); FI;
                              (* idt operand to error_code is 0 because selector is used *)
                         NewSS  2 bytes loaded from (TSS base + TSSstackAddress + 2);
                         NewESP  2 bytes loaded from (TSS base + TSSstackAddress);
              FI;
              IF NewSS is NULL
                    THEN #TS(EXT); FI;
              IF NewSS index is not within its descriptor-table limits
              or NewSS RPL  new code-segment DPL
                    THEN #TS(error_code(NewSS,0,EXT)); FI;
                    (* idt operand to error_code is 0 because selector is used *)
              Read new stack-segment descriptor for NewSS in GDT or LDT;
              IF new stack-segment DPL  new code-segment DPL
              or new stack-segment Type does not indicate writable data segment
                    THEN #TS(error_code(NewSS,0,EXT)); FI;
                    (* idt operand to error_code is 0 because selector is used *)
              IF NewSS is not present
                    THEN #SS(error_code(NewSS,0,EXT)); FI;
                    (* idt operand to error_code is 0 because selector is used *)
         ELSE (* IA-32e mode *)
              IF IDT-gate IST = 0
                    THEN TSSstackAddress  (new code-segment DPL « 3) + 4;
                    ELSE TSSstackAddress  (IDT gate IST « 3) + 28;
              FI;
              IF (TSSstackAddress + 7) > current TSS limit
                    THEN #TS(error_code(current TSS selector,0,EXT); FI;
                    (* idt operand to error_code is 0 because selector is used *)
              NewRSP  8 bytes loaded from (current TSS base + TSSstackAddress);
              NewSS  new code-segment DPL; (* NULL selector with RPL = new CPL *)
    FI;
    IF IDT gate is 32-bit
              THEN
                    IF new stack does not have room for 24 bytes (error code pushed)
                    or 20 bytes (no error code pushed)
                         THEN #SS(error_code(NewSS,0,EXT)); FI;
                         (* idt operand to error_code is 0 because selector is used *)
              FI
         ELSE
              IF IDT gate is 16-bit
                    THEN
                         IF new stack does not have room for 12 bytes (error code pushed)
                         or 10 bytes (no error code pushed);
                              THEN #SS(error_code(NewSS,0,EXT)); FI;
                              (* idt operand to error_code is 0 because selector is used *)
              ELSE (* 64-bit IDT gate*)
                    IF StackAddress is non-canonical
                         THEN #SS(EXT); FI; (* Error code contains NULL selector *)
         FI;
    FI;
    IF (IA32_EFER.LMA = 0) (* Not IA-32e mode *)
         THEN
              IF instruction pointer from IDT gate is not within new code-segment limits
                    THEN #GP(EXT); FI; (* Error code contains NULL selector *)
              ESP  NewESP;
              SS  NewSS; (* Segment descriptor information also loaded *)
         ELSE (* IA-32e mode *)
              IF instruction pointer from IDT gate contains a non-canonical address
                    THEN #GP(EXT); FI; (* Error code contains NULL selector *)
              RSP  NewRSP & FFFFFFFFFFFFFFF0H;
              SS  NewSS;
    FI;
    IF IDT gate is 32-bit
         THEN
              CS:EIP  Gate(CS:EIP); (* Segment descriptor information also loaded *)
         ELSE
              IF IDT gate 16-bit
                    THEN
                         CS:IP  Gate(CS:IP);
                         (* Segment descriptor information also loaded *)
                    ELSE (* 64-bit IDT gate *)
                         CS:RIP  Gate(CS:RIP);
                         (* Segment descriptor information also loaded *)
              FI;
    FI;
    IF IDT gate is 32-bit
              THEN
                    Push(far pointer to old stack);
                    (* Old SS and ESP3 words padded to 4 *)
                    Push(EFLAGS);
                    Push(far pointer to return instruction);
                    (* Old CS and EIP3 words padded to 4 *)
                    Push(ErrorCode); (* If needed, 4 bytes *)
              ELSE
                    IF IDT gate 16-bit
                         THEN
                              Push(far pointer to old stack);
                              (* Old SS and SP2 words *)
                              Push(EFLAGS(15-0]);
                              Push(far pointer to return instruction);
                              (* Old CS and IP2 words *)
                              Push(ErrorCode); (* If needed, 2 bytes *)
                         ELSE (* 64-bit IDT gate *)
                              Push(far pointer to old stack);
                              (* Old SS and SPeach an 8-byte push *)
                              Push(RFLAGS); (* 8-byte push *)
                              Push(far pointer to return instruction);
                              (* Old CS and RIPeach an 8-byte push *)
                              Push(ErrorCode); (* If needed, 8-bytes *)
              FI;
    FI;
    CPL  new code-segment DPL;
    CS(RPL CPL;
    IF IDT gate is interrupt gate
         THEN IF  0 (* Interrupt flag set to 0interrupts disabled *); FI;
    TF  0;
    VM  0;
    RF  0;
    NT  0;
END;
INTERRUPT-FROM-VIRTUAL-8086-MODE:
    (* Identify stack-segment selector for privilege level 0 in current TSS *)
    IF current TSS is 32-bit
         THEN
              IF TSS limit < 9
                    THEN #TS(error_code(current TSS selector,0,EXT)); FI;
                    (* idt operand to error_code is 0 because selector is used *)
              NewSS  2 bytes loaded from (current TSS base + 8);
              NewESP  4 bytes loaded from (current TSS base + 4);
         ELSE (* current TSS is 16-bit *)
              IF TSS limit < 5
                    THEN #TS(error_code(current TSS selector,0,EXT)); FI;
                    (* idt operand to error_code is 0 because selector is used *)
              NewSS  2 bytes loaded from (current TSS base + 4);
              NewESP  2 bytes loaded from (current TSS base + 2);
    FI;
    IF NewSS is NULL
         THEN #TS(EXT); FI; (* Error code contains NULL selector *)
    IF NewSS index is not within its descriptor table limits
    or NewSS RPL  0
         THEN #TS(error_code(NewSS,0,EXT)); FI;
         (* idt operand to error_code is 0 because selector is used *)
    Read new stack-segment descriptor for NewSS in GDT or LDT;
    IF new stack-segment DPL  0 or stack segment does not indicate writable data segment
         THEN #TS(error_code(NewSS,0,EXT)); FI;
         (* idt operand to error_code is 0 because selector is used *)
    IF new stack segment not present
         THEN #SS(error_code(NewSS,0,EXT)); FI;
         (* idt operand to error_code is 0 because selector is used *)
    IF IDT gate is 32-bit
         THEN
              IF new stack does not have room for 40 bytes (error code pushed)
              or 36 bytes (no error code pushed)
                    THEN #SS(error_code(NewSS,0,EXT)); FI;
                    (* idt operand to error_code is 0 because selector is used *)
         ELSE (* IDT gate is 16-bit)
              IF new stack does not have room for 20 bytes (error code pushed)
              or 18 bytes (no error code pushed)
                    THEN #SS(error_code(NewSS,0,EXT)); FI;
                    (* idt operand to error_code is 0 because selector is used *)
    FI;
    IF instruction pointer from IDT gate is not within new code-segment limits
         THEN #GP(EXT); FI; (* Error code contains NULL selector *)
    tempEFLAGS  EFLAGS;
    VM  0;
    TF  0;
    RF  0;
    NT  0;
    IF service through interrupt gate
         THEN IF = 0; FI;
    TempSS  SS;
    TempESP  ESP;
    SS  NewSS;
    ESP  NewESP;
    (* Following pushes are 16 bits for 16-bit IDT gates and 32 bits for 32-bit IDT gates;
    Segment selector pushes in 32-bit mode are padded to two words *)
    Push(GS);
    Push(FS);
    Push(DS);
    Push(ES);
    Push(TempSS);
    Push(TempESP);
    Push(TempEFlags);
    Push(CS);
    Push(EIP);
    GS  0; (* Segment registers made NULL, invalid for use in protected mode *)
    FS  0;
    DS  0;
    ES  0;
    CS:IP  Gate(CS); (* Segment descriptor information also loaded *)
    IF OperandSize = 32
         THEN
              EIP  Gate(instruction pointer);
         ELSE (* OperandSize is 16 *)
              EIP  Gate(instruction pointerAND 0000FFFFH;
    FI;
    (* Start execution of new routine in Protected Mode *)
END;
INTRA-PRIVILEGE-LEVEL-INTERRUPT:
    (* PE = 1DPL = CPL or conforming segment *)
    IF IA32_EFER.LMA = 1 (* IA-32e mode *)
         IF IDT-descriptor IST  0
              THEN
                    TSSstackAddress  (IDT-descriptor IST « 3) + 28;
                    IF (TSSstackAddress + 7) > TSS limit
                         THEN #TS(error_code(current TSS selector,0,EXT)); FI;
                         (* idt operand to error_code is 0 because selector is used *)
                    NewRSP  8 bytes loaded from (current TSS base + TSSstackAddress);
    FI;
    IF 32-bit gate (* implies IA32_EFER.LMA = 0 *)
         THEN
              IF current stack does not have room for 16 bytes (error code pushed)
              or 12 bytes (no error code pushed)
                    THEN #SS(EXT); FI; (* Error code contains NULL selector *)
         ELSE IF 16-bit gate (* implies IA32_EFER.LMA = 0 *)
              IF current stack does not have room for 8 bytes (error code pushed)
              or 6 bytes (no error code pushed)
                    THEN #SS(EXT); FI; (* Error code contains NULL selector *)
         ELSE (* IA32_EFER.LMA = 164-bit gate*)
                    IF NewRSP contains a non-canonical address
                         THEN #SS(EXT); (* Error code contains NULL selector *)
         FI;
    FI;
    IF (IA32_EFER.LMA = 0) (* Not IA-32e mode *)
         THEN
              IF instruction pointer from IDT gate is not within new code-segment limit
                    THEN #GP(EXT); FI; (* Error code contains NULL selector *)
         ELSE
              IF instruction pointer from IDT gate contains a non-canonical address
                    THEN #GP(EXT); FI; (* Error code contains NULL selector *)
              RSP  NewRSP & FFFFFFFFFFFFFFF0H;
    FI;
    IF IDT gate is 32-bit (* implies IA32_EFER.LMA = 0 *)
         THEN
              Push (EFLAGS);
              Push (far pointer to return instruction); (* 3 words padded to 4 *)
              CS:EIP  Gate(CS:EIP); (* Segment descriptor information also loaded *)
              Push (ErrorCode); (* If any *)
         ELSE
              IF IDT gate is 16-bit (* implies IA32_EFER.LMA = 0 *)
                    THEN
                         Push (FLAGS);
                         Push (far pointer to return location); (* 2 words *)
                         CS:IP  Gate(CS:IP);
                         (* Segment descriptor information also loaded *)
                         Push (ErrorCode); (* If any *)
                    ELSE (* IA32_EFER.LMA = 164-bit gate*)
                         Push(far pointer to old stack);
                         (* Old SS and SPeach an 8-byte push *)
                         Push(RFLAGS); (* 8-byte push *)
                         Push(far pointer to return instruction);
                         (* Old CS and RIPeach an 8-byte push *)
                         Push(ErrorCode); (* If needed, 8 bytes *)
                         CS:RIP  GATE(CS:RIP);
                         (* Segment descriptor information also loaded *)
              FI;
    FI;
    CS(RPL CPL;
    IF IDT gate is interrupt gate
         THEN IF  0; FI; (* Interrupt flag set to 0; interrupts disabled *)
    TF  0;
    NT  0;
    VM  0;
    RF  0;
END;



Which part of overkill that you didn't understand?

Show me your int3 interrupt handlers that you've written so far for your polished programs / library.
Post 03 May 2017, 23:30
View user's profile Send private message Reply with quote
revolution
When all else fails, read the source


Joined: 24 Aug 2004
Posts: 14672
Location: Origae-6

system error wrote:
... invoking Trap Gate, Task Gate, setting up IVT, entering etc etc?

I don't see your argument here. So what if the CPU does all of those tasks? The program has already run into a bad place. I'd much rather it tell me immediately that something bad has happened than continue on and perform other actions until finally some other exception occurs later. Because at some point you'll get the exception triggered, so it might as well happen earlier than later. I don't care if int3 takes one million cycles to run, I'd still much rather have that than other unknown random actions happening.
Post 04 May 2017, 00:19
View user's profile Send private message Visit poster's website Reply with quote
system error



Joined: 01 Sep 2013
Posts: 471

revolution wrote:
I'd still much rather have that than other unknown random actions happening.



Maybe that's why we have software interrupt or software exception handlers. I've given you Java example. One faulty move, other process can still run. With improper int3 handling, you can't even move a muscle because it takes control of your hardware state. I mean, do I have to teach you this basic thing? Just because Windows has its own internal debugger (which is a SOFTWARE INTERRUPT HANDLER anyway), you should not be fooled into believing that 0xCC can be handled like a breeze because it is not. What Windows does is to hide the implementation details of int3 (by using its own internal debugger for the users) so that they 'feel' comfortable working with 0xCC in and around their apps. Without such convenience, I believe people won't be tough talk on using 0xCC.
Post 04 May 2017, 04:06
View user's profile Send private message Reply with quote
Display posts from previous:
Post new topic Reply to topic

Jump to:  
Goto page 1, 2  Next

< 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


Powered by phpBB © 2001-2005 phpBB Group.

Main index   Download   Documentation   Examples   Message board
Copyright © 2004-2016, Tomasz Grysztar.