 |
forums.ps2dev.org Homebrew PS2, PSP & PS3 Development Discussions
|
| View previous topic :: View next topic |
| Author |
Message |
ragnarok2040
Joined: 09 Aug 2006 Posts: 230
|
Posted: Sun Aug 19, 2007 5:40 am Post subject: strings with twin characters |
|
|
I ran across a bug that kind of worries me, but I haven't been able to reproduce it in a test case. I wrote some browsing code for a larger project, for browsing directories. It had been working, but, all of a sudden, I was unable to use strcat() to append a "/" to a folder with twin characters at the end, e.g. 123466, FOLDRR. I'm not sure if the problem lies in the strcpy() of fio_dirent_t member "name" copying an invalid character at the end or if it's actually strcat()'s fault. Replacing strcat() with sprintf() fixed the problem I was having, though, so it makes me think the strcpy() is fine. I appended the code of what exactly happens when browsing directories. This code works though, so I commented the parts that were giving me the trouble. I used the same optimization flags as well, thinking it might have been an optimization error.
| Code: |
#include <stdio.h>
#include <string.h>
#include <libmc.h>
#include <sifrpc.h>
#include <sys/stat.h>
#include <loadfile.h>
#include <sbv_patches.h>
typedef struct {
char nothing[32];
int dircheck;
char something[256];
} entries;
void LoadModules(void)
{
int ret;
ret = SifLoadModule("rom0:SIO2MAN", 0, NULL);
if (ret < 0) {
printf("Failed to load module: SIO2MAN");
}
ret = SifLoadModule("rom0:MCMAN", 0, NULL);
if (ret < 0) {
printf("Failed to load module: MCMAN");
}
ret = SifLoadModule("rom0:MCSERV", 0, NULL);
if (ret < 0) {
printf("Failed to load module: MCSERV");
}
}
static inline char* strzncpy(char *d, char *s, int l) { d[0] = 0; return strncat(d, s, l); }
int main() {
SifInitRpc(0);
sbv_patch_enable_lmb();
sbv_patch_disable_prefix_check();
LoadModules();
mcInit(MC_TYPE_MC);
entries FileEntry[2048];
char path[4096];
strcpy(path,"mc0:/");
int i,dd;
int n = 0;
fio_dirent_t buf;
dd = fioDopen(path);
while(fioDread(dd,&buf)) { //list entries
if((FIO_SO_ISDIR(buf.stat.mode)) && (!strcmp(buf.name,".") || !strcmp(buf.name,"..")))
continue;
if(FIO_SO_ISDIR(buf.stat.mode)) {
FileEntry[n].dircheck = 1;
strcpy(FileEntry[n].something,buf.name); //filename
strzncpy(FileEntry[n].nothing,FileEntry[n].something,31); //displayname
n++;
}
if(n>2046) break;
}
fioDclose(dd);
for(i=0;i<(n-1);i++) {
strcpy(path,"mc0:/");
if(strchr(path,'/') == NULL) {}; //used for checking if coming from device list in browser
strcat(path,FileEntry[i].something); //folder "i" selected
strcat(path,"/"); //append delimiter which wasn't working e.g. "mc0:/FOLDRR" would still be in path afterwards
if(i>0) //append something else to it which wasn't working
strcat(path,FileEntry[i-1].something);
else
strcat(path,FileEntry[i+1].something);
strcat(path,"/");
printf("%s\n",path);
}
return 0;
}
|
Makefile
| Code: |
# _____ ___ ____ ___ ____
# ____| | ____| | | |____|
# | ___| |____ ___| ____| | \ PS2DEV Open Source Project.
#-----------------------------------------------------------------------
# Copyright 2001-2004, ps2dev - http://www.ps2dev.org
# Licenced under Academic Free License version 2.0
# Review ps2sdk README & LICENSE files for further details.
#
# $Id: Makefile.sample 1150 2005-06-12 11:42:08Z pixel $
EE_BIN = stringy.elf
EE_CFLAGS = -O3 -Winline -ffast-math -finline-functions -fstrict-aliasing -funsigned-char -fomit-frame-pointer -funroll-loops
EE_LIBS = -lmc -lpatches
EE_OBJS = stringy.o
all: $(EE_BIN)
clean:
rm -f *.elf *.o
include $(PS2SDK)/samples/Makefile.pref
include $(PS2SDK)/samples/Makefile.eeglobal
|
|
|
| Back to top |
|
 |
Jim

Joined: 02 Jul 2005 Posts: 487 Location: Sydney
|
Posted: Sun Aug 19, 2007 9:15 am Post subject: |
|
|
| Code: |
typedef struct {
char nothing[32];
int dircheck;
char something[256];
} entries;
int main() {
...
entries FileEntry[2048];
...
|
That's nearly 600Kb of stack - is that OK?
Jim _________________ http://www.dbfinteractive.com |
|
| Back to top |
|
 |
ragnarok2040
Joined: 09 Aug 2006 Posts: 230
|
Posted: Sun Aug 19, 2007 9:51 am Post subject: |
|
|
It seems to be alright, someone was able to browse more than a thousand or so files on his usb drive with it like that without any problems. I'd originally had it defined as entries *FileEntry = (entries*)malloc(sizeof(entries)*2048); but changed it recently to that as a local variable since malloc wasn't having any trouble allocating the memory, but calling free(FileEntry) kept randomly crashing the program.
Two instances of the function is all that's called at any one time... maybe I should just define it globally so it doesn't take up more than a megabyte of memory... Thanks for the heads up :D. |
|
| Back to top |
|
 |
Jim

Joined: 02 Jul 2005 Posts: 487 Location: Sydney
|
Posted: Sun Aug 19, 2007 10:07 am Post subject: |
|
|
The malloc is a much better idea. Just because malloc can allocate it doesn't mean there's enough room on the stack for the same sized object - you probably need to tell the linker or the C runtime startup that you need such a huge stack - I don't know what the default is on PS2SDK, but I bet it's more like 64Kb than 1Mb.
If your stack overflowed, it could easily cause the problem you are having.
Jim _________________ http://www.dbfinteractive.com |
|
| Back to top |
|
 |
ragnarok2040
Joined: 09 Aug 2006 Posts: 230
|
Posted: Sun Aug 19, 2007 3:41 pm Post subject: |
|
|
Looks like the stack size for ps2sdk is 128 Kb. I am overflowing the stack so I changed my code back, heh.
What I don't understand is why would it only affects double character ending strings only. It doesn't affect directories with 32 characters in the name or browsing multiple levels up or down. Compiling with -fstack-check didn't cause it to exit the program either. But I think that only works on Linux... and if not, I don't think I was browsing enough entries to overflow the stack, heh. Maybe that guy was using an older version :/.
Oh yeah, the bug still happens whether I'm malloc'ing FileEntry or relying on the stack.
I checked the disassembly and I think I came across something strange...
The first one is from the program using strcat with the bug and the second with sprintf().
| Code: |
error with strcat
940: 3c040000 lui a0,0x0
944: 24840000 addiu a0,a0,0
948: 0c000000 jal 0 <RomBrowserInput>
working sprintf
940: 3c060000 lui a2,0x0
944: 24c60000 addiu a2,a2,0
948: 00c0202d move a0,a2
94c: 24e70024 addiu a3,a3,36
950: 268510c8 addiu a1,s4,4296
954: 0c000000 jal 0 <RomBrowserInput>
|
I think this is the output from a working strcat operation that does something similar...
| Code: |
14: 3c030000 lui v1,0x0
18: 8c820008 lw v0,8(a0)
1c: 24650010 addiu a1,v1,16
20: 03a0202d move a0,sp
24: ffa70000 sd a3,0(sp)
28: 0c000000 jal 0 <main>
|
I don't know assembly at all, but 0x0 would probably be the null terminator and I think the rest would be modification of the strings... |
|
| Back to top |
|
 |
Jim

Joined: 02 Jul 2005 Posts: 487 Location: Sydney
|
Posted: Mon Aug 20, 2007 10:08 am Post subject: |
|
|
You can't tell anything from that.
You can easily check strcat. Right at the beginning of main() just do something like
| Code: |
char test[2048] = "FOLDRR";
strcat(test, "banana");
|
and then check the result. If it's wrong, strcat is wrong. If it's right, then something else you are doing is wrong.
Jim _________________ http://www.dbfinteractive.com |
|
| Back to top |
|
 |
ragnarok2040
Joined: 09 Aug 2006 Posts: 230
|
Posted: Mon Aug 20, 2007 5:08 pm Post subject: |
|
|
I did post in my first post that I couldn't reproduce it outside of the program. That last copy & paste of assembly was from a program just like the one you posted, except I used a string length of 256. You're right that the assembly code didn't say anything, heh. I'm not sure exactly what else I can be doing with strcpy and strcat that would make it go wrong only on specific strings with double character non-numeral endings, anyway.
The exact behavior of the bug with "1234MM" being the string inside the array of directory names is:
path[4096];
(output from my program is intermixed)
strcpy(path,"mc0:/");
path = mc0:/
strcat(path,"1234MM");
path = mc0:/1234MM
strcat(path,"/");
path = mc0:/1234MM
strcat(path,"filename.fil");
path = mc0:/1234MM
.. selected
there is a /
there is no /
strcpy(path,"path");
path = path
Edit:
It disappears if I use word, doubleword or quadword alignments (pretty much anything 16 bytes and over), so I'm guessing strcat tries to strcpy the second string to the first string at the wrong byte for some reason, maybe the byte after the null terminator... since I think if the position were earlier it would cut into the first string's characters or maybe the strcpy there is incrementing a spot prior to copying the string for some reason.
Edit2:
I am able to reproduce the exact behavior I'm getting by setting the first character of the src string to a null terminator prior to using strcat to append it to the dest string.
Edit3:
Having the dest string aligned to 16 bytes caused my program to start producing random crashes every once in a while when entering those specific directories... |
|
| Back to top |
|
 |
|
|
You cannot post new topics in this forum You cannot reply to topics in this forum You cannot edit your posts in this forum You cannot delete your posts in this forum You cannot vote in polls in this forum
|
Powered by phpBB © 2001, 2005 phpBB Group
|