2016-03-23 7 views
3

Ich versuche, einige "Legacy" -Code zu aktualisieren, um die neuesten Sicherheitsupdates für MSVC zu erfüllen, und stehe vor einigen Problemen bei der Migration von _vsnprintf zu _vsnprintf_s.Abrufen erforderlicher Pufferlänge mit Secure _vsnprintf_s

Insbesondere war ich _vsnprintf mit einem Null-Puffer und Null für die Zählung/Länge Aufruf, das Ergebnis zu erzielen, einen Puffer der benötigten Größe (return value + 1) Zuordnen und dann _vsnprintf wieder mit dem neu zugewiesenen Puffer aufrufen und bekannt richtige Größe:

size_t length = _vsntprintf(nullptr, 0, mask, params); 
TCHAR *final = new TCHAR [length + 1]; 
_vsntprintf(final, length + 1, mask, params); 

Dieses Verhalten ist documented on MSDN:

Wenn die Puffergröße durch Zählung angegeben ist nicht ausreichend groß, um die Ausgabe von Format und argptr angegeben zu enthalten, die Rückgabewert von vsnprintf ist die Anzahl der Zeichen, die geschrieben würden, wenn die Anzahl ausreichend groß wäre. Wenn der Rückgabewert größer als count - 1 ist, wurde die Ausgabe abgeschnitten.

Ich versuche, das gleiche mit _vsnprintf_s, aber its documentation does not contain the same zu tun. Er sagt stattdessen

Wenn die Speicherung der Daten zu speichern, erforderlich und eine abschließende Null überschreitet sizeOfBuffer, der ungültige Parameter-Handler aufgerufen wird, wie in Parameter Validation beschrieben, es sei denn Zählung _TRUNCATE, wobei in diesem Fall so viel von der Zeichenfolge Wie passt in Puffer wird geschrieben und -1 zurückgegeben.

Der Versuch, es sowieso mit dem folgenden:

size_t length = _vsntprintf_s(nullptr, 0, 0, mask, params); 

Dies führt zu einer "Länge" von Null. Wenn Sie in _TRUNCATE (-1) als die Zählung statt passieren, wird die folgende Behauptung fehlschlägt:

Expression: Puffer = nullptr & & buffer_count> 0

Ich nehme es möglich ist, _set_invalid_parameter_handler außer Kraft zu setzen und irgendwie herausfinden, wie lang sein sollte, aber es muss einen einfacheren Weg geben?

+0

Dies ist kein C-Code. – Olaf

+0

@Olaf Sorry, das hätte C++ sein sollen. Ich ärgerte mich über das "Sicherheit-verbessert-CRT" -Tag, dass ich diesen Tippfehler verpasste. Glaubst du wirklich, das war es wert, downvotiert zu werden? –

+0

In der Zeile 'size_t length = _vsntprintf (nullptr, 0, 0, mask, params);', meinst du '_vsntprintf_s'? – TriskalJM

Antwort

6
size_t length = _vscprintf(mask, va_list); 
TCHAR *final = new TCHAR [length + 1]; 
_vsntprintf_s(final, length, _TRUNCATE, mask, va_list); 
+1

Das ist perfekt. Eine schöne, saubere Lösung. '_vscprintf' sollte auf der' _vsnprintf_s'-Seite, imho, dokumentiert werden. –

-1

Wie wäre Ihre eigene vsnprintf Variante rollen, die „verletzen die Regeln“ nicht die Länge erhalten:

int 
printf_size(const char *fmt,int count,va_list ap) 
{ 
    char buf[2000000]; 
    int len; 

    len = vsnprintf_s(buf,sizeof(buf),count,fmt,ap); 

    return len; 
} 

Seit dem zurück wird [höchstwahrscheinlich] werden weniger als sizeof(buf) sollten Sie sein fein.

Oder tun:

int 
printf_size(const char *fmt,int count,va_list ap) 
{ 
    char *buf; 
    int siz; 
    int len; 

    for (siz = 2000000; ; siz <<= 1) { 
     buf = malloc(siz); 
     len = vsnprintf_s(buf,siz,count,fmt,ap); 
     free(buf); 
     if (len < siz) 
      break; 
    } 

    return len; 
} 

Oder macht eine One-Stop-Shop-Funktion:

int 
sprintf_secure(char **buf,const char *fmt,int count,va_list ap) 
{ 
    char *bp; 
    int siz; 
    int len; 

    for (siz = 2000000; ; siz <<= 1) { 
     bp = malloc(siz); 
     len = vsnprintf_s(bp,siz,count,fmt,ap); 
     if (len < siz) 
      break; 
    } 

    bp = realloc(bp,len + 1); 

    *buf = bp; 

    return len; 
} 
+0

Danke für den Vorschlag, aber zufällige Zuweisung von 4MB Daten jedes Mal, wenn jemand meine Funktion anruft, ist eine Art Overkill. –

+0

Nun, verwenden Sie einfach die [erste] Stack-Version. Es gibt keine Zuteilung. Mit einem 2MB Puffer [10.000 sollte genug sein] sollte es das maximale Stapellimit nicht überschreiten. Oder sprintf_secure, aber beginnen Sie mit 1000 (oder 100) - es ist ein Tuning-Parameter, der dynamisch angepasst werden kann, indem Sie die tatsächlich verwendete Länge beobachten. BTW, basierend auf deinem Kommentar, habe ich einfach angenommen, dass das '* cprintf' aus den gleichen Gründen unbrauchbar wäre, sonst hätte ich die Antwort wahrscheinlich nicht gepostet. –

+0

Nein, die Secure-CRT-Funktionen ersetzen nur Operationen, die das Schreiben in einen zuvor zugewiesenen Puffer beinhalten. Eine Funktion wie vscprintf, die eine ganze Zahl zurückgibt, wäre nicht betroffen. –