On 2025-03-28 Pali Rohár wrote:
> On Saturday 22 March 2025 15:35:48 Lasse Collin wrote:
> > I verified in MSYS2's CLANG64 environment that -fsanitize=function
> > and -fsanitize=undefined do complain on Windows too.
> > 
> > file1.c:
> > 
> >     struct foo { int x; };
> >     int foo(struct foo *f);
> > 
> >     int main(void)
> >     {
> >         struct foo f = { 5 };
> >         int (*cb)(struct foo *f) = &foo;
> >         return cb(&f);
> >     }
> > 
> > file2.c:
> > 
> >     struct wrongname { int x; };
> > 
> >     int foo(struct wrongname *f)
> >     {
> >         return f->x;
> >     }
> > 
> > Testing:
> > 
> >    $ clang -O2 -fsanitize=undefined file1.c file2.c
> >    $ ./a
> >    file1.c:8:12: runtime error: call to function foo through pointer
> >    to incorrect function type 'int (*)(struct foo *)'
> >    (C:\msys64\home\....): note: foo defined here SUMMARY:
> >    UndefinedBehaviorSanitizer: undefined-behavior file1.c:8:12
> > 
> > If I build with "clang -O2 -fsanitize=cfi -flto" then the program
> > terminates with an illegal instruction instead.  
> 
> Ah, that is bad :-( Is not there some attribute to mark the function
> to add either structure name alias or turn off the runtime check for
> particular function?

I tried __attribute__((no_sanitize("cfi"))) and a few related
attributes. These work if used in the calling code, for example, on the
main() function in the above example. They don't help if applied to the
foo() declaration or definition. That is, the attributes don't seem
helpful here. Even if an attribute worked, it would poke a small hole
in the CFI protection.

Clang supports -fsanitize-ignorelist=filename.txt. With a very quick
try, it worked for -fsanitize=undefined but not for =cfi. But even if
it worked, this wouldn't really feel like a real solution.

    https://clang.llvm.org/docs/SanitizerSpecialCaseList.html

The sanitizer issue should be easy enough to fix properly. Each stat*.c
would, if needed, define _USE_32BIT_TIME_T and _FILE_OFFSET_BITS, and
then use "struct stat". That is, a similar method as my ftw patch used.

-int __cdecl stat64i32(const char *_Filename, struct _stat64i32 *_Stat)
+int __cdecl stat64i32(const char *_Filename, struct stat *_Stat)
 {
   char *_path = __mingw_fix_stat_path(_Filename);
   if (_path == NULL)
     return -1;
-  int ret = _stat64i32(_path, _Stat);
+  int ret = _stat64i32(_path, (struct _stat64i32 *)_Stat);

Adding the cast doesn't create a new strict aliasing issue; it only
makes the existing strict aliasing issue easier to see. So the above
idea fixes the -fsanitize problem but doesn't make the strict aliasing
situation better or worse. (I'm not sure right now if attributes can
help with that in this specific situation.)

There would also need to be fifth stat*.c file to provide a stat64()
function that uses "struct stat64". Other than the argument type, it
would be ABI compatible with regular stat64(). Assuming that -fsanitize
isn't a worry when directly calling MS CRT functions, only non-UCRT
builds would need the fifth stat*.c file.

#ifdef _UCRT
// stat64 is redirected in the .def file.
int __cdecl stat64(const char *_Filename, struct stat64 *_Stat);
#else
// Wrapper that converts the argument type to keep -fsanitize=function
// and -fsanitize=cfi happy.
int __cdecl stat64(const char *_Filename, struct stat64 *_Stat)
    __MINGW_ASM_CALL(__mingw_lfs_stat64);
#endif

> > OK, sorry. I had thought that the misuse of __CRT__NO_INLINE in
> > <sys/stat.h> was a somewhat severe bug, and fixing it as a side
> > effect could have been easy enough here.  
> 
> As other people pointed out, this needs to be fixed. I proposed one
> solution in other thread.
> 
> > In <ftw.h> I used __CRT_BUILDING_FTW. It can be renamed, of course.
> > A separate macro for <ftw.h> is convenient because building ftw*.c
> > depends on the seeing the correct stat struct and function from
> > <sys/stat.h>.  
> 
> I'm proposing alternative solution to this ftw problem, without
> introduction of any new macro. What do you think about it?
> 
> It is attached in ftw.patch file.

I suspect it doesn't work if we care about -fsanitize. The problem
is the callback function provided by the application. It takes a
pointer to struct stat, but with your patch the struct name is not
"stat".

On the other hand, -fsanitize with ftw() (but not nftw()) is already
broken because do_it() always calls the callback function with four
arguments. It's easy, if slightly ugly, to fix by adding another
function pointer to struct ctx_t, and then picking the right one with a
helper function.

If the above aren't a concern, your patch seems fine to me. :-) However,
I'm not sure what else it truly fixes than the ABI symbol names and
avoids new macros in the public headers. If my idea (2) from the other
thread was used, the beginning of ftw.c could have

    #define __MINGW_CRTBLD_STAT_VISIBLE

and <ftw.h> could hide the declarations behind _CRTBLD. So
__CRT_BUILDING_FTW wouldn't be needed. The only new macro would be
__MINGW_CRTBLD_STAT_VISIBLE.

> Also it fixes the ABI problem which you have there. In both 32-bit
> msvcrt and 32-bit UCRT builds is "stat" symbol ABI compatible with
> stat32. Even the fact that 32-bit UCRT uses stat function API
> compatible with stat64i32. So it is needed to distinguish between ABI
> symbols and API functions. In your original change you used 32-bit
> UCRT ftw symbol compatible with ftw64i32, which breaks API/ABI
> compatibility with stat symbol and struct stat.

Thanks! I should have copied the #ifdefs from your stat*.c files.

-- 
Lasse Collin


_______________________________________________
Mingw-w64-public mailing list
Mingw-w64-public@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/mingw-w64-public

Reply via email to