4

Problem :

Inactive write to stack inside an external procedure

Code :

Inside an extern procedure which asks the user to input a string then returning it to the main through the stack.

String is defined at the data segment with a name different than that of data segment inside main procedure file.

Data_segment_name_ext segment para
ORG 10H
Str  DB 20,?,20 DUP (?)
Data_segment_name_ext ends

and stack segment declaration :

Stack_segment_name segment para stack
db 64 dup(0) ;define your stack segment
Stack_segment_name ends

originally at the procedure beginning I declared it public and set BP to Stack top :

PUBLIC MyProc
Code_segment_name segment
MyProc PROC FAR
assume SS:Stack_segment_name,CS:Code_segment_name,DS:Data_segment_name_ext
PUSH BP 
MOV BP,SP

String is being read by function AH=0x0A interrupt 0x21

LEA DX,Str
MOV AH,0Ah
INT 21H

Trying to save the string into the stack using the following loop :

MOV CX,22 ; The string length
MOV SI,00 ; used as an index
TRA1:
DEC BP
MOV AL,Str[SI] ; Str is defined in the data segment
MOV [BP],AL 
INC SI
LOOP TRA1

debugging the program using code view 4.01 and MASM 6.11 results in the following :

1-String is read correctly and stored in the DS and offset Str [Actual string starts two bytes after max length,actual count]

2-Strange behavior in the writing string into the stack:

Let SP originally=0xBA after the loop BP=0xA4 (i.e 0xBA-0x16(String length)) Dumping Stack Segment at SS:0xA4 Showing a garbage data 8 bytes before SS:SP and correct data is being written beyond this.

if the str='ABCDEFGHIJ' only 'GHIJ' are saved on the stack

>DB SS:0xA4

SS:00A4 00 00 00 00 00 00 00 00   00 00 4A 49 48 47 E7 05  ..........JIHG.. 
SS:00B4 7E 00 FD 05 02 02 00 00   0A 00 0C 06 B8 E7 05 8E  ~...............

Note : 060C:000A was CS:IP before executing the far call to the extern procedure and is pushed successfully @SP=0xC0(i.e. at SS:0xBC,SS:0xBD,SS:0xBE,SS:0xBF)

3-Replacing MOV [BP],AL by MOV [BP],33h results in the same behavior ;33h is not written in the first 8 bytes around old TOS

4-Enforcing SS (i.e. MOV SS:[BP],AL ) is also helpless as same behavior occurs

I know that I could return the parameters in alternative ways but why this behavior happens ?

  • 4
    You should move `SP` to allocate the memory first. Otherwise other things such as interrupts or even your debugger might use that space for their own purpose. – Jester Dec 12 '15 at 14:34
  • Thanks it works , I added _SUB SP,22_ before the loop to allocate this space and then before the last two lines of the code _POP BP_ and _RETF_ I add_ADD SP,22_ – Abdelrhman Abotaleb Dec 12 '15 at 14:44
  • @Jester it works well till the statement _ADD SP,22_ when de-allocating this space so as to make _POP BP_ and _RETF_ works well. the same region around the stack altered to values same as before – Abdelrhman Abotaleb Dec 12 '15 at 14:58
  • 1
    Ok after digging I do the allocation by re positioning SP before calling the extern procedure – Abdelrhman Abotaleb Dec 12 '15 at 16:06
  • Are you trying to access this string after the `MyProc` is called? – alxersov Dec 12 '15 at 17:00
  • Yes I uses the stack to do passing the string from inside the procedure MyProc to the Main procedure @aersh it works well after first allocating space before calling MyProc using _SUB SP,22_ Then inside the far MyProc I access this location using the offset _BP +6+22_ where _MOV BP,SP_ is done first and 6 accounts for the _ CALL FAR MyProc_ and _PUSH BP_ done at the procedure beginning – Abdelrhman Abotaleb Dec 14 '15 at 01:47
  • I've added a picture to my answer that shows what happens with the stack. – alxersov Dec 14 '15 at 21:32

2 Answers2

2

SP register indicates the current position of the top of the stack. Everything below this adders is not in the stack yet. You can not use addresses below SP to store any data because it will be overwritten once anything is pushed into the stack (e.g. when an interrupt is generated).

To store local variables in the stack you decrement SP (SUB SP, 22). This is the same as pushing 22 bytes into the stack.

At the end of the procedure you need to deallocate the local variables. To do this you increment SP (ADD SP, 22). This removes 22 bytes from the stack.

Once you've done this, local variables can no longer be accessed.

The following picture demonstrates the stack state:

Stack

  1. The beginning of MyProc execution.
  2. The space for local variables allocated.
  3. A string copied to the stack.
  4. Local variables removed from stack.
  5. An interrupt occurred: the state of the stack inside the interrupt handler.
  6. Interrupt handler finished execution, returned to MyProc.

Starting with the step 4 memory used for the "ABC..HJ" string no longer belongs to the MyProc. On the next step this memory is used to handle an interrupt.

It is not possible to save data in the current stack frame an return it this way from a procedure.

alxersov
  • 137
  • 1
  • 2
  • 8
  • Usually you use `MOV SP, BP` or `LEAVE` to deallocate the local variables in 16-bit x86 code. There's no 16-bit addressing mode that uses SP, so you have to use BP to access variables allocated on the stack. – Ross Ridge Dec 12 '15 at 18:05
  • Your last sentence is definitely invalidated if you look at the answer given by @user3144770 where the code does exactly that! – Fifoernik Dec 16 '15 at 14:49
  • 1
    Please note that in the answer given by @user3144770 the buffer is allocated in stack _before_ calling MyProc. The buffer is not allocated in the stack frame of MyProc. I updated the last sentence in my answer to clarify this. – alxersov Dec 18 '15 at 21:39
2

Since the purpose of your procedure MyProc is to return a string through the stack you inevitably have to store it above the return address pushed on the stack by the call instruction. This code does that:

sub  sp, 22
call MyProc

Now instead of inputting via an extra buffer in DS you could simplify the task and input directly in the space that you vacated on the stack.

mov  ax, 20
sub  sp, ax
push ax         ;This sets up the correct buffer DOS expects
call MyProc
...
MyProc PROC FAR
assume SS:Stack_segment_name,CS:Code_segment_name
PUSH BP 
MOV  BP,SP
push ds
push ss
pop  ds
lea  dx, [bp+6]
;String is being read by function AH=0x0A interrupt 0x21
MOV AH,0Ah
INT 21H
pop  ds
...

I see that you've set up a stack of only 64 bytes. If you plan to store strings in the stack I advice you to increase this size.

Sep Roland
  • 33,889
  • 7
  • 43
  • 76