Announcement

Collapse
No announcement yet.

Help in determining size of passed ASCIIZ parameter

Collapse
X
 
  • Filter
  • Time
  • Show
Clear All
new posts

  • Michael Mattias
    replied
    it doesn't say what happens when you define a parameter in a procedure as being ASCIIZ but with no maximum length specified
    Lots of stuff unsaid in the help but that is not important right now..

    What it is, is, "Address of a buffer" .. same as when you use " sz AS ASCIIZ * somevalue"

    The difference is, when you state the length with " * somevalue", the compiler will limit how much data will be read or written; when you don't use "* somevalue" it won't... it just follows your instructions, reading until it hits a $NUL (or GPFs because it did not find one until it exceeded the owned memory), or writing WHATEVER YOU TELL IT and appending a $NUL... or GPFing because you tried to write into memory you don't own.

    Btw in this example? Overwriting that UDT is not going to get you a GPF, because that is stored on the stack, and by definition you own the stack. Unfortunately, you are probably already using the area of the stack where your overrun is writing... for other variables. Oops. You have variables changing value mysteriously? Well, this is the easy way to do that.

    As I said... when you use ambiguous-length ASCIIZ variables, you are in charge - and responsible. Great tool - once you learn how to use it.

    MCM

    Leave a comment:


  • Jeff Blakeney
    replied
    In the PBWin help file for ASCIIZ string, it doesn't say what happens when you define a parameter in a procedure as being ASCIIZ but with no maximum length specified. As ASCIIZ strings have a maximum length (as opposed to a fixed length), I take it PB decided to allow this sort of thing so that you could use the same function with ASCIIZ strings that have different maximum lengths but are leaving it up to the programmer to make sure they don't write past the end of the string passed in. I believe this would usually be done by defining the procedure like this:

    Code:
    FUNCTION Get_Code(zCode AS ASCIIZ, lSize AS LONG) AS LONG
    and calling it like this:

    Code:
    Get_Code(TabData.Currency_Code, SIZEOF(TabData.Currency_Code))
    And just to clarify something,

    Originally posted by Carlo Pagani View Post
    The point is you run the program is that the ">" overwrites the Period so the period changes 200910 to 200704 which, as it happens, gave me a few more grey hairs
    The ">" is not messing up the Period member of the UDT, it is the null being put at the end of the string overwriting the least significant byte of Period. 200910 = &H000310CE and 200704 = &H00031000. Notice that the last byte, &HCE, has been replaced with null, &H00.

    Leave a comment:


  • Michael Mattias
    replied
    Code:
    FUNCTION Get_Code(zCode AS ASCIIZ) AS LONG
      zCode = "<ANY>"
    END FUNCTION
    When you do this...
    Code:
     Get_Code(TabData.Currency_Code)
    .. which is an "ASCIIZ * 5" variable, you are passing 4 chars plus a nul

    When you write to it..
    Code:
    zCode = "<ANY>"
    .. you are writing SIX characters (five plus $nul) , meaning you are corrupting your data by overrunning the target field, meaning everything you do after that is pot-luck.

    Or maybe you are writing FIVE characters, the compiler assuming 'zCode' is initialized to $NUL... but even then you are corrupting your data, since you still are overwriting the REQUIRED $NULL at the end of the string.

    If you want to use ambiguous-length ASCIIZ or ASCIIZ PTR variables, you are 100% responsible for ensuring the target variable is of a sufficient size to hold whatever you are writing.

    This is a PB-compliler-version-independent Error 1 (Programmer Error).

    MCM

    Leave a comment:


  • Chris Holbrook
    replied
    I don't see the problem if you are writing the data. If you want to know the length of the string then you can determine it by populating the UDT along the following lines:

    Code:
    #compile exe
    #dim all
    type test
            a as asciz * 4
            b as integer
    end type
    
    function pbmain () as long
        local ttest as test
        
         ttest.a = "ttttt"
         ttest.b = 0
        ? varptr (ttest.a), varptr (ttest.b)
        ? "length of string should not exceed", _
            varptr (ttest.b) - varptr (ttest.a) -1
        waitkey$
    end function

    Leave a comment:


  • Carlo Pagani
    replied
    @MCM

    Are you saying this code would have worked in Version 6? My initial thoughts where that the compiler should know that I am passing TabData.Currency_Code which is ASCIIZ*5 so why overwrite the next element in the UDT. Then I concluded it is only passing a pointer to TabData.Currency_Code, so how can the function know the length?

    Leave a comment:


  • Michael Mattias
    replied
    ** edit - actually isbadreadptr will be no help at all, the data are probably inside valid memory, the problem is just field overflow within the UDT instance
    In this case, that's not a problem "if" you are managing the contents of the UDT members using only assignment statements ... at least if you are using any version of PB/Win greater than 6.0.

    When you assign a string or string expresssion to an ASCIIZ target, the compiler will never write more than SIZEOF(target)-1 characters of the source string/string expression, always reserving the last byte of the target for the required $NUL.

    If you are managing your UDT data via copymemory() or direct pointer moves which cannot be reduced to an assignment or as part of a UNION, all bets are off (as they should be!)

    (Prior to 6.10, this was not true. Been there, done that, reported the bug.)

    MCM

    Leave a comment:


  • Carlo Pagani
    replied
    Thanks Kev and all...

    @Kev - That's the conclusion I came to, but it helps to be affirmed before starting a massive "Search and Update" exercise.

    Leave a comment:


  • Kev Peel
    replied
    There is no reliable way to detect the size of a buffer address on it's own.

    In the following example, two alt. methods are shown: pass the size to the function (function A) or let the compiler enforce the size (function B)

    Code:
    #Compile Exe
    #Dim All
     
    Function Get_CodeA(zCode As Asciiz, ByVal dwSize As Dword) As Long
      zCode = Left$("<ANY>", dwSize-1)
    End Function
     
    Function Get_CodeB(zCode As Asciiz * 5) As Long
      zCode = "<ANY>"
    End Function
     
    Function PBMain () As Long
     
      Local z As Asciiz * 5
     
      Get_CodeA(z, SizeOf(z))
      ? "A: " + z
     
      Get_CodeB(z)
      ? "B: " + z
     
    End Function

    Leave a comment:


  • Carlo Pagani
    replied
    Hi Gösta

    Exactly... This code is to simulate a bug I had. I thought the complier would have known that TabData.Currency_Code could only contain 4 chrs plus a Null, but then if all the function is getting is a pointer to some place in memory rather than being told that it should be ASCIIZ * 5, I deduced the compiler cannot stop at 4 characters <ANY as it does when you do a simple Var = "<ANY>".

    The point is you run the program is that the ">" overwrites the Period so the period changes 200910 to 200704 which, as it happens, gave me a few more grey hairs

    Leave a comment:


  • Gösta H. Lovgren-2
    replied
    I'm not understanding something here, Carlo. You are sending a 4 character string ("<ANY" & chr$(0)) and returning a 5 char string ("<ANY>" & chr$(0)). How is GetCode supposed to do anything else?
    '
    Code:
    #Compile Exe
    #Dim All
    Type TabDataType
      Office_Code As Asciiz * 9
      Currency_Code As Asciiz * 5
      Period As Long
    End Type
     
    Function Get_Code(zCode As Asciiz) As Long
      zCode = "<ANY>"
    End Function
     
    Function PBMain () As Long
      Static TabData As TabDataType
      Static AnyString As String
      TabData.Period = 200910
      TabData.Office_Code = "1234567890"
      TabData.Currency_Code = "<ANY>"
     
      AnyString = "Pass 1:Currency = "+ TabData.Currency_Code + " Period=" + Format$(TabData.Period) & _
                  Using$(" len tbcc = # ", Len(TabData.Currency_Code))
      Get_Code(TabData.Currency_Code)
      AnyString = AnyString+$CrLf+ _
                 "Pass 2:Currency = " + TabData.Currency_Code + " Period=" + Format$(TabData.Period) & _
                  Using$(" len tbcc = # ", Len(TabData.Currency_Code))
     
      MsgBox AnyString,,"Content of AnyString"
    End Function
    '
    ============================
    "Just living is not enough.
    One must have sunshine,
    freedom,
    and a little flower."
    Hans Christian Anderson
    ============================

    Leave a comment:


  • Chris Holbrook
    replied
    Originally posted by Michael Mattias View Post
    about all you can do is inspect the characters one by one until either the charcter is NUL or the address is invalid ( IsBadReadPtr).
    ....after reading this: http://www.powerbasic.com/support/pb...t=IsBadReadPtr

    Dunno about variable length strings in UDTs, I think you have to use fixed-length ones.

    Carlo, are you creating this data, or are you importing it from elsewhere?

    ** edit - actually isbadreadptr will be no help at all, the data are probably inside valid memory, the problem is just field overflow within the UDT instance.

    Leave a comment:


  • Michael Mattias
    replied
    If you really want to measure how much data was passed in an ambiguous-length ("AS ASCIIZ") parameter, about all you can do is inspect the characters one by one until either the charcter is NUL or the address is invalid ( IsBadReadPtr).

    A function like lstrLen() will fail because it can hit the end of owned memory without having encountered a NUL.

    But if you want to handle variable-length strings, "AS STRING" might be a better choice than "AS ASCIIZ".


    MCM

    Leave a comment:


  • Carlo Pagani
    replied
    Hi Fred

    That's not my issue (was typing junk). If you compile, you'll see the bug I created for myself in the value of the period.

    Cheers

    Leave a comment:


  • Fred Harris
    replied
    field big enough?

    Hi Carlo!

    The Office_Code field of TabDataType is Asciiz*9 which is only big enough to hold 8 characters. There are 10 in this string...

    TabData.Office_Code = "1234567890"

    And this >> "<ANY>" -- won't fit in an Asciiz*5 string either.

    ???

    Fred
    Last edited by Fred Harris; 27 Oct 2009, 04:31 PM.

    Leave a comment:


  • Help in determining size of passed ASCIIZ parameter

    Hi All, given the code below, I have a generic routine to read parameters that are of varying lengths and all form part of a UDT. Because they are of varying length, the Get_Code function parameter is defined without a length and so the last character in this example corrupts my period.

    Is there any way without using an interim assign or extra parameter that the function can determine the size of the passed parameter? I suspect not but ask anyway...

    Code:
    #COMPILE EXE
    #DIM ALL
    
    TYPE TabDataType
      Office_Code AS ASCIIZ * 9
      Currency_Code AS ASCIIZ * 5
      Period AS LONG
    END TYPE
    
    
    FUNCTION Get_Code(zCode AS ASCIIZ) AS LONG
      zCode = "<ANY>"
    END FUNCTION
      
    FUNCTION PBMAIN () AS LONG
      STATIC TabData AS TabDataType
      STATIC AnyString AS STRING
      TabData.Period = 200910
      TabData.Office_Code = "1234567890"
      TabData.Currency_Code = "<ANY>"
      
      AnyString = "Pass 1:Currency = "+ TabData.Currency_Code+" Period="+FORMAT$(TabData.Period)
      Get_Code(TabData.Currency_Code)
      AnyString = AnyString+$CRLF+"Pass 2:Currency = "+ TabData.Currency_Code+" Period="+FORMAT$(TabData.Period)
      
      MSGBOX AnyString,,"Content of AnyString"
    
    END FUNCTION
    Thanks - Carlo
Working...
X
😀
🥰
🤢
😎
😡
👍
👎