2012-02-28 34 views
0

(聲明:我意識到這是文本的一個巨大的牆,但我已經盡了我熬下來的要領。如果你熟悉的libtiff這不是很複雜的問題)迷茫中添加自定義標記時實施_TIFFVGetField

我問過的libtiff郵件列表這個問題了,但我想我可以有一個很好的機會,在這裏,以及如果存在任何人誰與圖書館工作。

我在這裏使用的文件加入我自己的內置標籤庫:http://libtiff.maptools.org/addingtags.html

所以,我加入到tif_dirinfo.c頂部像這樣定義的數組TIFFFieldInfo一個條目:

{ TIFFTAG_CUSTOM_XXX, 4, 4, TIFF_SLONG, FIELD_XXX, 1, 0, "XXX" }, 

我再添加一個字段中tif_dir.h定義的TIFFDirectory結構:

typedef struct { 
    /* ... */ 
    int32 td_xxx[4]; 
} TIFFDirectory; 

現在我繼續根據指示修改了_TIFFVSetField_TIFFVGetField。這是我遇到問題的地方。

在模仿模式已經存在於庫(見TIFFTAG_YCBCRSUBSAMPLING實施,這是類似於我在做什麼),我下面的代碼添加到_TIFFVGetField

/* existing, standard tag for reference */ 
case TIFFTAG_YCBCRSUBSAMPLING: 
     *va_arg(ap, uint16*) = td->td_ycbcrsubsampling[0]; 
     *va_arg(ap, uint16*) = td->td_ycbcrsubsampling[1]; 
     break; 
/* my new tag */ 
case TIFFTAG_CUSTOM_XXX: 
     *va_arg(ap, int32*) = td->td_xxx[0]; 
     *va_arg(ap, int32*) = td->td_xxx[1]; 
     *va_arg(ap, int32*) = td->td_xxx[2]; 
     *va_arg(ap, int32*) = td->td_xxx[3]; 
     break; 

至於我能告訴這是完全不正確的。這裏的意圖是根據int數組填充可變參數列表中的輸入。一個可取之處是,在va_list提供的參數是該類型的int32總是和YcBr代碼使用了兩個int16的。所以,它的工作原理,但我不能複製該實現。

_TIFFVGetField最終從TIFFWriteNormalTagtif_dirwrite.c中調用。相關的代碼是這樣的:

case TIFF_LONG: 
    case TIFF_SLONG: 
    case TIFF_IFD: 
     if (fip->field_passcount) { 
      uint32* lp; 
      if (wc == (uint16) TIFF_VARIABLE2) { 
       TIFFGetField(tif, fip->field_tag, &wc2, &lp); 
       TDIRSetEntryCount(tif,dir, wc2); 
      } else { /* Assume TIFF_VARIABLE */ 
       TIFFGetField(tif, fip->field_tag, &wc, &lp); 
       TDIRSetEntryCount(tif,dir, wc); 
      } 
      if (!TIFFWriteLongArray(tif, dir, lp)) 
       return 0; 
      } else { 
       if (wc == 1) { 
        uint32 wp; 
        /* XXX handle LONG->SHORT conversion */ 
        TIFFGetField(tif, fip->field_tag, &wp); 
        TDIRSetEntryOff(tif,dir, wp); 
       } else { 
       /* ---------------------------------------------------- */ 
       /* this is the code that is called in my scenario  */ 
       /* ---------------------------------------------------- */ 
        uint32* lp; 
        TIFFGetField(tif, fip->field_tag, &lp); 
        if (!TIFFWriteLongArray(tif, dir, lp)) 
         return 0; 
       } 
      } 
      break; 

因此,未初始化的指針lp聲明,並將其地址傳遞給TIFFGetField。這反過來設置所使用的va_list(與lp作爲唯一的參數),並調用TIFFVGetField,它調用_TIFFVGetField與供給va_list和指針到未初始化的指針。

這裏有兩個問題。

首先,這是圖書館如何提取數據(我的代碼,但同樣,下面的模式已經存在)

*va_arg(ap, int32*) = td->td_xxx[0]; 

這似乎不正確。它將原始指針設置爲int的值。我推斷,也許在我遵循的示例(TIFFTAG_YCBCRSUBSAMPLING)中,這些整數實際上是地址。所以好吧,但即使是這樣,但還有其他問題。

該庫調用va_argsN次,其中N是數組中元素的數量。從我看到的變量參數列表中只包含一個參數(指針的地址)。這是(在開始時重要的一點),每標準定義的操作:

如果沒有實際的一個參數,或者如果類型是不符合 實際的一個參數的類型兼容(根據推廣默認參數促銷),的行爲是未定義的。

正確的版本將是

*va_arg(ap, int32**) = td_xxx; 

這將設置先前未初始化的指針到數組中,這是有效的。我不喜歡它指向數據本身而不是副本,但不管怎樣;至少它不會崩潰,並給我正確的結果。

我在這裏擔心的是我錯過了一些微妙的東西。這個軟件很舊,被許多人使用。因此,調用這個bug對我來說就像是指責編譯器崩潰一樣,這是幾乎總是錯誤的

但是,我無法推斷出這種方式是正確的,特別是當庫被多次調用時,庫如何寫入va_arg的返回值。

任何幫助將不勝感激。提前致謝。

回答

0

因此,這裏的答案最終結果是libtiff依賴於UB。雖然這在技術上是UB,我找不到的va_arg的實現,並沒有做這樣的事情:

(*(t *)((ap += _INTSIZEOF(t)) - _INTSIZEOF(t))) 

所以,只要t小於原來的參數大小(因爲它是在這裏) ,您可以安全地多次呼叫va_arg

我最終只是設置了參數,導致我的數據,它的工作原理。我不喜歡直接訪問標題數據本身,但是不用以顯着方式更改庫,這是我唯一的選擇。