Lubuntu 13.04 64 bit, g++ 4.7, SFML2 from github (very recent build).
#include <SFML/Graphics.hpp>
int main()
{
sf::Texture tex;
text.loadFromFile("data/"); //CRASH if data folder actually exists
//alternative version:
text.loadFromFile("data"); //CRASH if data folder actually exists
}
There will be seg fault on loadFromFile when the dir actually exist. When the folder "data" doesn't exist in app's location there is no seg fault. It may be linux-only as well.
Call stack:
0 0x00007ffff78bf66c stbi_load
1 0x00007ffff78c33f4 sf::priv::ImageLoader::loadImageFromFile(std::string const&, std::vector<unsigned char, std::allocator<unsigned char> >&, sf::Vector2<unsigned int>&)
2 0x00007ffff78c8843 sf::Texture::loadFromFile(std::string const&, sf::Rect<int> const&)
I encountered this bug in following situation: (so it's a bug which may affect some ppl in rare situations)
sf::Texture tex;
std::string textureDir = "data/";
std::string textureName = map.getTextureName(..); // by error in map file, it returned empty string
tex.loadFromFile(textureDir+textureName);
What I expect in above case is that loadFromFile will return false and I will have an empty texture.
However, it simply crashed.
Remember that it must be existing folder as the function argument. When I set:
textureDir = "muhahadfjs2343434/";
Then there is no crash and expected output in console:
Failed to load image "muhahadfjs2343434/". Reason : Unable to open file
I hope that you will look into it. I can provide more debug info if you want.
When I link SFML libs in debug mode there is no crash and I get:
Failed to load image "data". Reason : Image not of any known type, or corrupt
The crash happens only with release SFML libs.
Very strange. So I can't provide you the line of crash because with debug libs there is no crash.
EDIT:
But looking at the loadFromFile() source I know the line of crash:
106: unsigned char* ptr = stbi_load(filename.c_str(), &width, &height, &channels, STBI_rgb_alpha);
It is the last function called in release mode.
I have no idea what STBI is. But it may be upstream bug in release mode.
EDIT2:
I see that STBI is packaged with SFML source and I think it has no active bug tracker. So it looks like it's the issue we need to fix ourselves. It works in debug mode, so we need to find out what is the difference between release and debug mode in STBI.
I've been on it for a long while:
This is some schrodinger bug, I can't accurately track when it occurs and when not, I tried -g, -O1, -O2 but in the end results were inconsistent, sometimes it happens, sometimes not(because it involves ub by accessing undefined ptr, see below)
Directory open for reading as a file seems to always(both FILE and ifstream) return that it read 0 bytes and has size of 2^31-1.
-sf::Shader.loadFromFile attempts to allocate 2^31-1 which throws std::bad_alloc(I have 2 gbs of ram) you probably should catch that exception up in loadFromFile and print what() to sf::err() and return false from load.
-sf::Image stbi attepts to decrement by one a pointer that has values null, 0x1, sometimes valid adresses, in the program adress space, it's just uninit ptr
typedef struct{ uint32 img_x
, img_y
; int img_n
, img_out_n
; stbi_io_callbacks io
; void *io_user_data
; int read_from_callbacks
; int buflen
; uint8 buffer_start
[128]; uint8 *img_buffer
, *img_buffer_end
;//these 2 ptrs are important uint8 *img_buffer_original
;} stbi
;unsigned char *stbi_load_from_file
(FILE
*f
, int *x
, int *y
, int *comp
, int req_comp
){ stbi s
; start_file
(&s
,f
); return stbi_load_main
(&s
,x
,y
,comp
,req_comp
);}static void start_file
(stbi
*s
, FILE
*f
){ start_callbacks
(s
, &stbi_stdio_callbacks
, (void *) f
);}static void start_callbacks
(stbi
*s
, stbi_io_callbacks
*c
, void *user
){ s
->io
= *c
; s
->io_user_data
= user
; s
->buflen
= sizeof(s
->buffer_start
); s
->read_from_callbacks
= 1; s
->img_buffer_original
= s
->buffer_start
; refill_buffer
(s
);// goes into there with these 2 pointers with undefined values in them}//the callback in refill buffer is that one, it always returns 0 for directories open as FILEstatic int stdio_read
(void *user
, char *data
, int size
){ return (int) fread(data
,1,size
,(FILE
*) user
);}static void refill_buffer
(stbi
*s
){ int n
= (s
->io.
read)(s
->io_user_data
,(char*)s
->buffer_start
,s
->buflen
); if (n
== 0) {//we go here // at end of file, treat same as if from memory s
->read_from_callbacks
= 0; s
->img_buffer
= s
->img_buffer_end
-1;//this wasnt ever initialized, sometimes its null, sometimes its 0x1, idk why *s
->img_buffer
= 0;//this line might do the segfault, writing to 0xffffffff or 0x0, in general writing into random space } else { s
->img_buffer
= s
->buffer_start
; s
->img_buffer_end
= s
->buffer_start
+ n
; }}
now if we hit into own memory, we are kind of ok and go to this function,
tatic unsigned char *stbi_load_main(stbi *s, int *x, int *y, int *comp, int req_comp)
{
if (stbi_jpeg_test(s)) return stbi_jpeg_load(s,x,y,comp,req_comp);
if (stbi_png_test(s)) return stbi_png_load(s,x,y,comp,req_comp);
if (stbi_bmp_test(s)) return stbi_bmp_load(s,x,y,comp,req_comp);
if (stbi_gif_test(s)) return stbi_gif_load(s,x,y,comp,req_comp);
if (stbi_psd_test(s)) return stbi_psd_load(s,x,y,comp,req_comp);
if (stbi_pic_test(s)) return stbi_pic_load(s,x,y,comp,req_comp);
#ifndef STBI_NO_HDR
if (stbi_hdr_test(s)) {
float *hdr = stbi_hdr_load(s, x,y,comp,req_comp);
return hdr_to_ldr(hdr, *x, *y, req_comp ? req_comp : *comp);
}
#endif
// test tga last because it's a crappy test!
if (stbi_tga_test(s))
return stbi_tga_load(s,x,y,comp,req_comp);
return epuc("unknown image type", "Image not of any known type, or corrupt");
}
-jpeg gets 'marker' m which if not 0xd8 will return error:
#define SOI(x) ((x) == 0xd8)
if (!SOI(m)) return e("no SOI","Corrupt JPEG");
-png, bmp and gif check for the three letters of their respective format in first bytes
-psd checks for "8BPS"
-pic checks for "\x53\x80\xF6\x34"
-hdr checks for "#?RADIANCE\n" but I'm not sure if SFML doesn't define it out, but it doesn't matter
-tga discards one byte, then returns if second byte is >1 or third is unequal to any of 1 2 3 9 10 11
Since we have 0 as first and only byte, and in case of tga we run out and call refill which 'refills' with 0 again so we are basically endless stream of 0s and fail all tests. I'd say that's a bug in stbi.
-sf::SoundBuffer and sf::Music, "Failed to open sound file "." (File contains data in an unknown format.)", I don't have source of sndfile but it seems it's immune to the fact that data can't be read from seemingly ok long file
-sf::Font "Failed to load font "." (failed to create the font face)" I guess freetype is immune too..
In any case this needs more testing but shader can be fixed right away,
stbi too:
static void start_callbacks(stbi *s, stbi_io_callbacks *c, void *user)
{
s->io = *c;
s->io_user_data = user;
s->buflen = sizeof(s->buffer_start);
s->read_from_callbacks = 1;
s->img_buffer_original = s->buffer_start;
s->img_buffer_end=s->buffer_start+1;//this line fixes it because now we use the first byte from out buffer_start[128]
refill_buffer(s);
}
I think..
But I might have made mistakes, and these really need more work:
-check freetype reactions and safety
-check sndfile reactions and safety
-check behavior of directory in FILE * on various *nixes(linux definitely affected at least my lovely Fedora and netrick's lubuntu, maybe other distros and macs too?)
-just use stat or dirent or something to avoid opening directory like that?