2012-02-28 1 views
0

(면책 조항 :이 내용은 방대한 텍스트 벽이라는 것을 알고 있지만, 필자는 본질에 익숙해지기 위해 최선을 다했다. 매우 복잡한 질문입니다.맞춤 태그를 추가 할 때 _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을 수정했습니다. 이것이 내가 문제를 일으킨 곳입니다. 지금까지 내가 할 수있는

/* 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; 

: 라이브러리에 이미 존재하는 패턴 (내가 뭐하는 거지 유사하다 TIFFTAG_YCBCRSUBSAMPLING의 구현을 참조)를 흉내 낸

, 나는 _TIFFVGetField에 다음 코드를 추가 이것이 완전히 틀렸다는 것을 말하십시오. int 배열을 기반으로 가변 인수 목록에 입력을 채우기위한 것입니다. 한 가지 유예의 축복은 va_list에 제공된 인수가 항상 int32 유형이고 YcBr 코드가 int16 2 개를 사용한다는 것입니다. 그래서, 작동하지만, 그 구현을 복사 할 수 없습니다.

은 결국 TIFFWriteNormalTag에서 tif_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; 

것이다. 나는 그것이 복사본이 아닌 데이터 그 자체를 가리키고 있다는 점을 좋아하지 않는다. 적어도 충돌을 일으키지 않고 올바른 결과를 제공합니다.

여기 내 관심사는 미묘한 뭔가를 놓치고 있다는 것입니다. 이 소프트웨어는 오래되었고 많은 사람들이 사용했습니다. 따라서이 버그를 호출하는 것은 컴파일러에서 충돌을 비난하는 것처럼 느낍니다. 이는 입니다. 거의 항상 잘못되었습니다.

그러나 올바른 방법, 특히 라이브러리가 두 번 이상 호출 될 때 va_arg이 반환하는 값에 대한 쓰기 방법을 추론 할 수는 없습니다.

도움을 주시면 감사하겠습니다. 미리 감사드립니다.

답변

0

그래서 여기에 대한 답은 궁극적으로 libtiff가 UB에 의존하고 있다는 결론을 내 렸습니다. (이 여기로) 한 t 원래 인수 크기보다 작으로, 그래서

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

: 기술적으로 UB하지만,이 같은 것을하지 않았다 va_arg의 구현을 찾을 수 없습니다 va_arg 번으로 안전하게 전화 할 수 있습니다.

필자는 데이터를 가져 오는 포인터에 대한 인수를 설정하고 작동합니다. 헤더 데이터 자체에 직접 액세스하는 것을 좋아하지 않지만 중요한 방법으로 라이브러리를 변경하지 않고 이것이 유일한 옵션이었습니다.