@@ -330,12 +330,12 @@ namespace Js
330
330
typedef FinalizableICUObject<UPluralRules *, uplrules_close> FinalizableUPluralRules;
331
331
332
332
template <typename TExecutor>
333
- static void EnsureBuffer (_In_ TExecutor executor, _In_ Recycler *recycler, _Outptr_result_buffer_(returnLength) char16 **ret, _Out_ int *returnLength, _In_ int firstTryLength = 8)
333
+ static void EnsureBuffer (_In_ TExecutor executor, _In_ Recycler *recycler, _Outptr_result_buffer_(returnLength) char16 **ret, _Out_ int *returnLength, _In_ bool allowZeroLengthStrings = false, _In_ int firstTryLength = 8)
334
334
{
335
335
UErrorCode status = U_ZERO_ERROR;
336
336
*ret = RecyclerNewArrayLeaf (recycler, char16, firstTryLength);
337
337
*returnLength = executor (reinterpret_cast <UChar *>(*ret), firstTryLength, &status);
338
- AssertOrFailFastMsg ( *returnLength > 0 , " Executor reported needing negative bytes " );
338
+ AssertOrFailFast (allowZeroLengthStrings ? *returnLength >= 0 : *returnLength > 0 );
339
339
if (ICU_BUFFER_FAILURE (status))
340
340
{
341
341
AssertOrFailFastMsg (*returnLength >= firstTryLength, " Executor reported buffer failure but did not require additional space" );
@@ -2665,6 +2665,9 @@ DEFINE_ISXLOCALEAVAILABLE(PR, uloc)
2665
2665
);
2666
2666
}
2667
2667
2668
+ // We intentionally special-case the following two calls to EnsureBuffer to allow zero-length strings.
2669
+ // See comment in GetPatternForSkeleton.
2670
+
2668
2671
char16 *formatted = nullptr ;
2669
2672
int formattedLen = 0 ;
2670
2673
if (!toParts)
@@ -2673,7 +2676,7 @@ DEFINE_ISXLOCALEAVAILABLE(PR, uloc)
2673
2676
EnsureBuffer ([&](UChar *buf, int bufLen, UErrorCode *status)
2674
2677
{
2675
2678
return udat_format (*dtf, date, buf, bufLen, nullptr , status);
2676
- }, scriptContext->GetRecycler (), &formatted, &formattedLen);
2679
+ }, scriptContext->GetRecycler (), &formatted, &formattedLen, /* allowZeroLengthStrings */ true );
2677
2680
return JavascriptString::NewWithBuffer (formatted, formattedLen, scriptContext);
2678
2681
}
2679
2682
@@ -2683,7 +2686,7 @@ DEFINE_ISXLOCALEAVAILABLE(PR, uloc)
2683
2686
EnsureBuffer ([&](UChar *buf, int bufLen, UErrorCode *status)
2684
2687
{
2685
2688
return udat_formatForFields (*dtf, date, buf, bufLen, fpi, status);
2686
- }, scriptContext->GetRecycler (), &formatted, &formattedLen);
2689
+ }, scriptContext->GetRecycler (), &formatted, &formattedLen, /* allowZeroLengthStrings */ true );
2687
2690
2688
2691
JavascriptLibrary *library = scriptContext->GetLibrary ();
2689
2692
JavascriptArray* ret = library->CreateArray (0 );
@@ -2807,6 +2810,14 @@ DEFINE_ISXLOCALEAVAILABLE(PR, uloc)
2807
2810
2808
2811
char16 *formatted = nullptr ;
2809
2812
int formattedLen = 0 ;
2813
+
2814
+ // OS#17513493 (OSS-Fuzz 7950): It is possible for the skeleton to be a zero-length string
2815
+ // because [[Get]] operations are performed on the options object twice, according to spec.
2816
+ // Follow-up spec discussion here: https://github.com/tc39/ecma402/issues/237.
2817
+ // We need to special-case this because calling udatpg_getBestPatternWithOptions on an empty skeleton
2818
+ // will produce an empty pattern, which causes an assert in EnsureBuffer by default.
2819
+ // As a result, we pass a final optional parameter to EnsureBuffer to say that zero-length results are OK.
2820
+ // TODO(jahorto): re-visit this workaround and the one in FormatDateTime upon resolution of the spec issue.
2810
2821
EnsureBuffer ([&](UChar *buf, int bufLen, UErrorCode *status)
2811
2822
{
2812
2823
return udatpg_getBestPatternWithOptions (
@@ -2818,7 +2829,7 @@ DEFINE_ISXLOCALEAVAILABLE(PR, uloc)
2818
2829
bufLen,
2819
2830
status
2820
2831
);
2821
- }, scriptContext->GetRecycler (), &formatted, &formattedLen);
2832
+ }, scriptContext->GetRecycler (), &formatted, &formattedLen, /* allowZeroLengthStrings */ true );
2822
2833
2823
2834
INTL_TRACE (" Best pattern '%s' will be used for skeleton '%s' and langtag '%s'" , formatted, skeleton->GetSz (), langtag->GetSz ());
2824
2835
0 commit comments