• Re: Error handling in __djgpp_nearptr_enable()

    From [via djgpp@delorie.com]" @21:1/5 to Pali on Thu Mar 21 08:31:16 2024
    To: cwsdpmi@earthlink.net (Charles Sandmann)
    Copy: dj@delorie.com
    Copy: sezeroz@gmail.com
    Copy: djgpp@delorie.com

    Date: Wed, 20 Mar 2024 21:33:30 +0100
    From: Pali <pali@pali.im>
    Cc: djgpp@delorie.com

    Hello, I observed strange issue that __djgpp_nearptr_enable() function
    when running in NTVDM on Windows XP, on failure let segment limit in
    some undefined/intermediate state.

    Test case:

    #include <stdio.h>
    #include <dpmi.h>
    #include <go32.h>
    #include <sys/nearptr.h>
    #include <sys/exceptn.h>

    int main() {
    unsigned long addr;
    if (__dpmi_get_segment_base_address(_my_ds(), &addr) == 0)
    printf("OLD BASE: 0x%lx\n", addr);
    printf("OLD LIMIT: 0x%lx\n", __dpmi_get_segment_limit(_my_ds()));
    printf("CALLING __djgpp_nearptr_enable()\n");
    if (__djgpp_nearptr_enable())
    printf("SUCCESS\n");
    else
    printf("FAILED\n");
    if (__dpmi_get_segment_base_address(_my_ds(), &addr) == 0)
    printf("NEW BASE: 0x%lx\n", addr);
    printf("NEW LIMIT: 0x%lx\n", __dpmi_get_segment_limit(_my_ds()));
    return 0;
    }

    It prints:

    OLD BASE: 0x29e0000
    OLD LIMIT: 0x9ffff
    CALLING __djgpp_nearptr_enable()
    FAILED
    NEW BASE: 0x29e0000
    NEW LIMIT: 0x7d60ffff

    I know that Windows NT kernel does not allow userspace to create a
    segment with access to kernel memory space (above 0x7fff0000 limit).
    So failure from the __djgpp_nearptr_enable() call in NTVDM is expected.
    But I was not expecting that DJGPP in some cases may let segment limit
    in some intermediate state.

    What about following DJGPP change? I think it can improve failure
    behavior of __djgpp_nearptr_enable() when 4 GB DS limit is not allowed.

    --- src/libc/pc_hw/nearptr/nearptr.c
    +++ src/libc/pc_hw/nearptr/nearptr.c
    @@ -14,7 +14,10 @@ int __djgpp_nearptr_enable(void)
    {
    if(!__dpmi_set_segment_limit(_my_ds(), 0xffffffffU)) {
    if(__dpmi_get_segment_limit(_my_ds()) != 0xffffffffU)
    + {
    + __dpmi_set_segment_limit(_my_ds(), __djgpp_selector_limit | 0xfff);
    return 0; /* We set it but DPMI ignored/truncated it */
    + }
    __dpmi_set_segment_limit(__djgpp_ds_alias, 0xffffffffU);
    __dpmi_set_segment_limit(_my_cs(), 0xffffffffU);
    _crt0_startup_flags |= _CRT0_FLAG_NEARPTR;

    What are the implications of this change, in plain English, in
    particular for DPMI hosts other than NTVDM? (It would be best to
    actually test this with at least the popular hosts, including CWSDPMI
    and perhaps also versions of Windows newer than XP that still support
    DPMI in a reasonable enough manner that allows running |DJGPP
    programs.)

    I'm CC'ing Charles, in the hope that he is available and can comment
    on this.

    Btw, the DJGPP FAQ explicitly says that __djgpp_nearptr_enable fails
    on Windows NT family (i.e. with NTVDM). So strictly speaking, a
    program that calls __djgpp_nearptr_enable in that case is shooting
    itself in the foot...

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From [via djgpp@delorie.com]" @21:1/5 to Pali on Fri Apr 12 16:37:47 2024
    Copy: cwsdpmi@earthlink.net
    Copy: dj@delorie.com
    Copy: sezeroz@gmail.com
    Copy: djgpp@delorie.com

    Date: Thu, 21 Mar 2024 18:49:23 +0100
    From: Pali <pali@pali.im>
    Cc: Charles Sandmann <cwsdpmi@earthlink.net>, dj@delorie.com,
    sezeroz@gmail.com, djgpp@delorie.com

    On Thursday 21 March 2024 08:31:16 Eli Zaretskii wrote:
    Hello, I observed strange issue that __djgpp_nearptr_enable() function when running in NTVDM on Windows XP, on failure let segment limit in
    some undefined/intermediate state.

    Test case:

    #include <stdio.h>
    #include <dpmi.h>
    #include <go32.h>
    #include <sys/nearptr.h>
    #include <sys/exceptn.h>

    int main() {
    unsigned long addr;
    if (__dpmi_get_segment_base_address(_my_ds(), &addr) == 0)
    printf("OLD BASE: 0x%lx\n", addr);
    printf("OLD LIMIT: 0x%lx\n", __dpmi_get_segment_limit(_my_ds()));
    printf("CALLING __djgpp_nearptr_enable()\n");
    if (__djgpp_nearptr_enable())
    printf("SUCCESS\n");
    else
    printf("FAILED\n");
    if (__dpmi_get_segment_base_address(_my_ds(), &addr) == 0)
    printf("NEW BASE: 0x%lx\n", addr);
    printf("NEW LIMIT: 0x%lx\n", __dpmi_get_segment_limit(_my_ds()));
    return 0;
    }

    It prints:

    OLD BASE: 0x29e0000
    OLD LIMIT: 0x9ffff
    CALLING __djgpp_nearptr_enable()
    FAILED
    NEW BASE: 0x29e0000
    NEW LIMIT: 0x7d60ffff

    I know that Windows NT kernel does not allow userspace to create a segment with access to kernel memory space (above 0x7fff0000 limit).
    So failure from the __djgpp_nearptr_enable() call in NTVDM is expected. But I was not expecting that DJGPP in some cases may let segment limit
    in some intermediate state.

    What about following DJGPP change? I think it can improve failure behavior of __djgpp_nearptr_enable() when 4 GB DS limit is not allowed.

    --- src/libc/pc_hw/nearptr/nearptr.c
    +++ src/libc/pc_hw/nearptr/nearptr.c
    @@ -14,7 +14,10 @@ int __djgpp_nearptr_enable(void)
    {
    if(!__dpmi_set_segment_limit(_my_ds(), 0xffffffffU)) {
    if(__dpmi_get_segment_limit(_my_ds()) != 0xffffffffU)
    + {
    + __dpmi_set_segment_limit(_my_ds(), __djgpp_selector_limit | 0xfff);
    return 0; /* We set it but DPMI ignored/truncated it */
    + }
    __dpmi_set_segment_limit(__djgpp_ds_alias, 0xffffffffU);
    __dpmi_set_segment_limit(_my_cs(), 0xffffffffU);
    _crt0_startup_flags |= _CRT0_FLAG_NEARPTR;

    What are the implications of this change, in plain English, in
    particular for DPMI hosts other than NTVDM?

    __djgpp_nearptr_enable() first tries to change DS limit to 4GB. If it
    fails then function returns error back to the client. If it success then
    it checks if DS limit is really set to 4GB. If the DS limit is not 4GB
    then function returns error back to the client (return 0; in above
    snipped).

    My change above does following: If DS limit is not 4GB then it is reset
    back to the previous value (used before trying to change it to 4GB) and
    after that returns error back to the client (return 0;).

    (It would be best to
    actually test this with at least the popular hosts, including CWSDPMI
    and perhaps also versions of Windows newer than XP that still support
    DPMI in a reasonable enough manner that allows running |DJGPP
    programs.)

    This change does not affect CWSDPMI, because CWSDPMI supports setting DS limit to 4GB and hence this code path is not executed.

    Also it does not affect any other DPMI host which return failure "on
    failure" and success "on success".

    Normally DPMI host should return error if it reject request for changing
    DS limit. But NTVDM is somehow special there, it neither reject request,
    nor accept it. Instead it changes DS limit to the value which is below
    the linear address 0x7fff0000; and returns success to the client. This
    is a reason why DJGPP here has that check if DS limit was really set to
    4GB.

    My change improve the situation here, independently of the failure
    reason (either __dpmi_set_segment_limit() failed or DPMI host returned success but not set limit to 4GB), the previous DS limit is not
    modified when function fails.

    So if application calls __djgpp_nearptr_enable(), it can ensure that on success the limit is 4GB and on failure the limit is not changed at all.

    I'm CC'ing Charles, in the hope that he is available and can comment
    on this.

    Btw, the DJGPP FAQ explicitly says that __djgpp_nearptr_enable fails
    on Windows NT family (i.e. with NTVDM). So strictly speaking, a
    program that calls __djgpp_nearptr_enable in that case is shooting
    itself in the foot...

    The NTVDM behavior is well documented and it is known that it does not
    allow to access linear memory above 0x7fff0000. And application does
    not know if is running under NTVDM or other DPMI host.

    OK, I've now installed this.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)