win32sysinfo.c doesn't understand DWORDLONG is unsigned and long

Matthew Sackman matthew@REDACTED
Thu Jan 28 18:02:27 CET 2010


void get_avail_mem_ext() {
    char answer[512];
    MEMORYSTATUSEX ms;
    ms.dwLength=sizeof(MEMORYSTATUSEX);
    GlobalMemoryStatusEx(&ms);
    sprintf(answer,"%d %I64d %I64d %I64d %I64d %I64d %I64d\n",
	    ms.dwMemoryLoad,
	    ms.ullTotalPhys,
	    ms.ullAvailPhys,
	    ms.ullTotalPageFile,
	    ms.ullAvailPageFile,
	    ms.ullTotalVirtual,
	    ms.ullAvailVirtual
	    );
    return_answer(answer);
    /*    
	DWORD     dwLength;
	DWORD     dwMemoryLoad;
	DWORDLONG ullTotalPhys;
	DWORDLONG ullAvailPhys;
	DWORDLONG ullTotalPageFile;
	DWORDLONG ullAvailPageFile;
	DWORDLONG ullTotalVirtual;
	DWORDLONG ullAvailVirtual;
    */
}

DWORD and DWORDLONG are unsigned and long. By printing with %I64d, you're
reinterpreting them as signed ints, leading to windows users with 4GB of
RAM being told they have negative amounts of RAM (or even 0). Eg:

$ cat foo.c
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>

main()
{
  uint64_t n = 4293308416;
  printf("%I64d\n", n);
  printf("%I64u\n", n);
  printf("%I64lu\n", n);
  return 0;
}

$ gcc -o foo foo.c && ./foo
                                                        -1658880
                                                      4293308416
                                                      4293308416

Elsewhere in win32sysinfo.c, there are %I64u format specifiers. It looks
like the only occurrence of %I64d is in the get_avail_mem_ext function.

Because they're long, I *think* they should be %I64lu. I think what's
happening is that the d makes it an int, thus the msb is set, hence the
negative answer. And indeed, setting n to 4294967297 (i.e. 2^32 + 1)
gives:

                                                               1
                                                               1
                                                      4294967297

Note that the above is running under a 64-bit Linux. I don't know if the
behaviour of %I64 is the same under Windows. I also don't know what size
an int is in a 32-bit application running on 64-bit windows. My C isn't
all that great. My reading is that the %I64 just says to use the system
locale, and to pad with whitespace up to 64 characters.

Matthew


More information about the erlang-bugs mailing list