[poppler] [PATCH] Fix GDir's handling of return values from FindFirstFile

Adam Batkin adam at batkin.net
Wed Apr 2 18:22:53 PDT 2008

> Can you please provide a better way to see this problem?
> Where did you got the binary from (KDE binary package?)?
> So far the patch looks ok, the POPPLER_DATADIR is a more or less serious 
> bug since we cannot really assume a fixed directory for the data.

I believe that I got the binary from the kde-windows project, though I'm 
not sure that is relevant here. If you work backwards through the code, 
you can *see* the problem.

Start in poppler/GlobalParams.cc, GlobalParams::scanEncodingDirs():
  655   GDir *dir;
  656   GDirEntry *entry;
  658   dir = new GDir(POPPLER_DATADIR "/nameToUnicode", gTrue);
  659   while (entry = dir->getNextEntry(), entry != NULL) {
  660     if (!entry->isDir()) {
  661       parseNameToUnicode(entry->getFullPath());
  662     }
  663     delete entry;
  664   }

So we see that a GDir is constructed on line 658, and on my system the 
directory that it "points" to doesn't exist.

So move on to the GDir constructor in goo/gfile.cc and skip down a few 

  645   hnd = FindFirstFile(tmp->getCString(), &ffd);

hnd is never touched again in the constructor, nor is ffd which was 
*supposed* to be populated by FindFirstFile(). At this point, hnd is 
either a valid handle, or else it is INVALID_HANDLE_VALUE. In my case, 

Jumping back to GlobalParams::scanEncodingDirs(), we see that 
dir->getNextEntry() is called, so let's take a look at that:
  675 #if defined(WIN32)
  676   if (hnd) {
  677     e = new GDirEntry(path->getCString(), ffd.cFileName, doStat);
  678     if (hnd  && !FindNextFile(hnd, &ffd)) {
  679       FindClose(hnd);
  680       hnd = NULL;
  681     }
  682   } else {
  683     e = NULL;
  684   }

On line 676 hnd==-1 so the if evalutes to true and a GDirEntry is 
constructed. The GDirEntry is the concatenation of path->getCString() 
(which is valid) and ffd.cFileName (in theory populated above in the 
call to FindFirstFile() in GDir::GDir() line 645 above) but ffd may 
point to garbage (in fact it does) since it was never properly populated!

Taking a quick look at GDirEntry's constructor:
  600 GDirEntry::GDirEntry(char *dirPath, char *nameA, GBool doStat) {
  601 #ifdef VMS
  602   char *p;
  603 #elif defined(WIN32)
  604   int fa;
  605 #elif defined(ACORN)
  606 #else
  607   struct stat st;
  608 #endif
  610   name = new GooString(nameA);
  611   dir = gFalse;
  612   fullPath = new GooString(dirPath);
  613   appendToPath(fullPath, nameA);
We see that fullPath and nameA are concatenated together (nameA is the 
invalid ffd.cFileName from above).

Back in GlobalParams::scanEncodingDirs(), Line 660 evaluates to true 
since "dir" (entry->isDir() is false) since it's not pointing to a 
directory and parseNameToUnicode(entry->getFullPath()) is called which 
is where we get that funky string from (entry->getFullPath() returns the 
fullPath value set in GDirEntry's constructor, line 613).

Then, GlobalParams::parseNameToUnicode() is called, which is where the 
error I saw came from:
  697   if (!(f = fopen(name->getCString(), "r"))) {
  698     error(-1, "Couldn't open 'nameToUnicode' file '%s'",
  699           name->getCString());
  700     return;
  701   }
Of course the fopen() fails, then that error is printed.

To tell you the truth, I don't know exactly what crashed my program. It 
could be the fopen(), it could be when it tries to concatenate the 
strings together, it could be when it prints the error (or it could be 
something completely different). No matter what though, it's certainly 
an serious bug, and it produces really ugly log messages. I don't yet 
have a full build environment for poppler itself, which is why I kept 
the patch so small and simple.

Hope this helps,

-Adam Batkin

More information about the poppler mailing list