More regular expression leak fix changes ... use older code (plus fixes).

This commit is contained in:
rfm 2024-11-21 21:05:34 +00:00
parent 087716fffa
commit 4c287f20b3
2 changed files with 195 additions and 82 deletions

View file

@ -1,7 +1,7 @@
2024-11-21 Richard Frith-Macdonald <rfm@gnu.org>
* NSRegularExpression: avoid leaks in ICU code by calling replacement
methods with a non-NULL destination.
* NSRegularExpression: avoid leaks in ICU code by calling more
primitive methods.
* NSAutoreleasePool: fix leaks with libobjc2 ARC methods when a pool
is in a context that we leave due to an exception.
* NSObject: add -trackOwnership to log the retain/release/dealloc of

View file

@ -35,9 +35,19 @@
* won't work because libicu internally renames all entry points with some cpp
* magic.
*/
#if !defined(HAVE_UREGEX_OPENUTEXT)
#if U_ICU_VERSION_MAJOR_NUM > 4 || (U_ICU_VERSION_MAJOR_NUM == 4 && U_ICU_VERSION_MINOR_NUM >= 4) || defined(HAVE_ICU_H)
#define HAVE_UREGEX_OPENUTEXT 1
#endif
#endif
/* Until the uregex_replaceAllUText() and uregex_replaceFirstUText() work
* without leaking memory, we can't use them :-(
* Preoblem exists on Ubuntu in 2024 with icu-74.2
*/
#if defined(HAVE_UREGEX_OPENUTEXT)
#undef HAVE_UREGEX_OPENUTEXT
#endif
#define NSRegularExpressionWorks
@ -158,7 +168,8 @@ NSRegularExpressionOptionsToURegexpFlags(NSRegularExpressionOptions opts)
NSDictionary *userInfo;
NSString *description;
description = [NSString stringWithFormat: @"The value “%@” is invalid.", aPattern];
description = [NSString
stringWithFormat: @"The value “%@” is invalid.", aPattern];
userInfo = [NSDictionary dictionaryWithObjectsAndKeys:
aPattern, @"NSInvalidValue",
@ -245,11 +256,46 @@ NSRegularExpressionOptionsToURegexpFlags(NSRegularExpressionOptions opts)
}
#endif
// Raise an NSInvalidArgumentException to match macOS behaviour.
if (!aPattern)
{
NSException *exp;
exp = [NSException exceptionWithName: NSInvalidArgumentException
reason: @"nil argument"
userInfo: nil];
RELEASE(self);
[exp raise];
}
[aPattern getCharacters: buffer range: NSMakeRange(0, length)];
regex = uregex_open(buffer, length, flags, &pe, &s);
if (U_FAILURE(s))
{
// FIXME: Do something sensible with the error parameter.
/* Match macOS behaviour if the pattern is invalid.
* Example:
* Domain=NSCocoaErrorDomain
* Code=2048 "The value “<PATTERN>” is invalid."
* UserInfo={NSInvalidValue=<PATTERN>}
*/
if (e)
{
NSDictionary *userInfo;
NSString *description;
description = [NSString
stringWithFormat: @"The value “%@” is invalid.", aPattern];
userInfo = [NSDictionary dictionaryWithObjectsAndKeys:
aPattern, @"NSInvalidValue",
description, NSLocalizedDescriptionKey,
nil];
*e = [NSError errorWithDomain: NSCocoaErrorDomain
code: NSFormattingError
userInfo: userInfo];
}
DESTROY(self);
return self;
}
@ -868,29 +914,35 @@ prepareResult(NSRegularExpression *regex,
{
// FIXME: We're computing a value that is most likely ignored in an
// expensive way.
NSInteger results = [self numberOfMatchesInString: string
options: opts
range: range];
UErrorCode s = 0;
UText dst = UTEXT_INITIALIZER;
UText txt = UTEXT_INITIALIZER;
UText replacement = UTEXT_INITIALIZER;
NSMutableString *ms = [NSMutableString string];
URegularExpression *r = setupRegex(regex, string, &txt, opts, range, 0);
NSInteger results = [self numberOfMatchesInString: string
options: opts
range: range];
UErrorCode s = 0;
UText txt = UTEXT_INITIALIZER;
UText replacement = UTEXT_INITIALIZER;
GSUTextString *ret = [GSUTextString new];
URegularExpression *r = setupRegex(regex, string, &txt, opts, range, 0);
UText *output = NULL;
UTextInitWithNSString(&replacement, template);
UTextInitWithNSMutableString(&dst, ms);
uregex_replaceAllUText(r, &replacement, &dst, &s);
uregex_close(r);
utext_close(&replacement);
utext_close(&txt);
utext_close(&dst);
output = uregex_replaceAllUText(r, &replacement, NULL, &s);
if (0 != s)
{
uregex_close(r);
utext_close(&replacement);
utext_close(&txt);
DESTROY(ret);
return 0;
}
[string setString: ms];
utext_clone(&ret->txt, output, TRUE, TRUE, &s);
[string setString: ret];
RELEASE(ret);
uregex_close(r);
utext_close(&txt);
utext_close(output);
utext_close(&replacement);
return results;
}
@ -899,26 +951,31 @@ prepareResult(NSRegularExpression *regex,
range: (NSRange)range
withTemplate: (NSString*)template
{
UErrorCode s = 0;
UText dst = UTEXT_INITIALIZER;
UText txt = UTEXT_INITIALIZER;
UText replacement = UTEXT_INITIALIZER;
NSMutableString *ms = [NSMutableString string];
URegularExpression *r = setupRegex(regex, string, &txt, opts, range, 0);
UErrorCode s = 0;
UText txt = UTEXT_INITIALIZER;
UText replacement = UTEXT_INITIALIZER;
UText *output = NULL;
GSUTextString *ret = [GSUTextString new];
URegularExpression *r = setupRegex(regex, string, &txt, opts, range, 0);
UTextInitWithNSString(&replacement, template);
UTextInitWithNSMutableString(&dst, ms);
uregex_replaceAllUText(r, &replacement, &dst, &s);
uregex_close(r);
utext_close(&replacement);
utext_close(&txt);
utext_close(&dst);
output = uregex_replaceAllUText(r, &replacement, NULL, &s);
if (0 != s)
{
uregex_close(r);
utext_close(&replacement);
utext_close(&txt);
DESTROY(ret);
return nil;
}
return ms;
utext_clone(&ret->txt, output, TRUE, TRUE, &s);
uregex_close(r);
utext_close(&txt);
utext_close(output);
utext_close(&replacement);
return AUTORELEASE(ret);
}
- (NSString*) replacementStringForResult: (NSTextCheckingResult*)result
@ -926,13 +983,13 @@ prepareResult(NSRegularExpression *regex,
offset: (NSInteger)offset
template: (NSString*)template
{
UErrorCode s = 0;
UText dst = UTEXT_INITIALIZER;
UText txt = UTEXT_INITIALIZER;
UText replacement = UTEXT_INITIALIZER;
NSMutableString *ms = [NSMutableString string];
NSRange range = [result range];
URegularExpression *r = setupRegex(regex,
UErrorCode s = 0;
UText txt = UTEXT_INITIALIZER;
UText replacement = UTEXT_INITIALIZER;
UText *output = NULL;
GSUTextString *ret = [GSUTextString new];
NSRange range = [result range];
URegularExpression *r = setupRegex(regex,
[string substringWithRange: range],
&txt,
0,
@ -940,18 +997,22 @@ prepareResult(NSRegularExpression *regex,
0);
UTextInitWithNSString(&replacement, template);
UTextInitWithNSMutableString(&dst, ms);
uregex_replaceFirstUText(r, &replacement, &dst, &s);
uregex_close(r);
utext_close(&dst);
utext_close(&txt);
utext_close(&replacement);
output = uregex_replaceFirstUText(r, &replacement, NULL, &s);
if (0 != s)
{
uregex_close(r);
utext_close(&replacement);
utext_close(&txt);
DESTROY(ret);
return nil;
}
return ms;
utext_clone(&ret->txt, output, TRUE, TRUE, &s);
utext_close(output);
uregex_close(r);
utext_close(&txt);
utext_close(&replacement);
return AUTORELEASE(ret);
}
#else
- (NSUInteger) replaceMatchesInString: (NSMutableString*)string
@ -964,31 +1025,51 @@ prepareResult(NSRegularExpression *regex,
NSInteger results = [self numberOfMatchesInString: string
options: opts
range: range];
UErrorCode s = 0;
uint32_t length = [string length];
uint32_t replLength = [template length];
unichar replacement[replLength];
int32_t outLength;
unichar *output;
NSString *out;
URegularExpression *r;
TEMP_BUFFER(buffer, length);
if (results > 0)
{
UErrorCode s = 0;
uint32_t length = [string length];
uint32_t replLength = [template length];
unichar replacement[replLength];
int32_t outLength;
URegularExpression *r;
TEMP_BUFFER(buffer, length);
r = setupRegex(regex, string, buffer, length, opts, range, 0);
[template getCharacters: replacement range: NSMakeRange(0, replLength)];
r = setupRegex(regex, string, buffer, length, opts, range, 0);
[template getCharacters: replacement range: NSMakeRange(0, replLength)];
outLength = uregex_replaceAll(r, replacement, replLength, NULL, 0, &s);
outLength = uregex_replaceAll(r, replacement, replLength, NULL, 0, &s);
if (0 == s || U_BUFFER_OVERFLOW_ERROR == s)
{
unichar *output;
s = 0;
output = NSZoneMalloc(0, outLength * sizeof(unichar));
uregex_replaceAll(r, replacement, replLength, output, outLength, &s);
out =
[[NSString alloc] initWithCharactersNoCopy: output
length: outLength
freeWhenDone: YES];
[string setString: out];
RELEASE(out);
s = 0; // May have been set to a buffer overflow error
output = NSZoneMalloc(0, (outLength + 1) * sizeof(unichar));
uregex_replaceAll(r, replacement, replLength,
output, outLength + 1, &s);
if (0 == s)
{
NSString *out;
out = [[NSString alloc] initWithCharactersNoCopy: output
length: outLength
freeWhenDone: YES];
[string setString: out];
RELEASE(out);
}
else
{
NSZoneFree(0, output);
results = 0;
}
}
else
{
results = 0;
}
uregex_close(r);
}
return results;
}
@ -1003,20 +1084,36 @@ prepareResult(NSRegularExpression *regex,
uint32_t replLength = [template length];
unichar replacement[replLength];
int32_t outLength;
unichar *output;
NSString *result = nil;
TEMP_BUFFER(buffer, length);
r = setupRegex(regex, string, buffer, length, opts, range, 0);
[template getCharacters: replacement range: NSMakeRange(0, replLength)];
outLength = uregex_replaceAll(r, replacement, replLength, NULL, 0, &s);
if (0 == s || U_BUFFER_OVERFLOW_ERROR == s)
{
unichar *output;
s = 0;
output = NSZoneMalloc(0, outLength * sizeof(unichar));
uregex_replaceAll(r, replacement, replLength, output, outLength, &s);
return AUTORELEASE([[NSString alloc] initWithCharactersNoCopy: output
length: outLength
freeWhenDone: YES]);
s = 0; // may have been set to a buffer overflow error
output = NSZoneMalloc(0, (outLength + 1) * sizeof(unichar));
uregex_replaceAll(r, replacement, replLength, output, outLength + 1, &s);
if (0 == s)
{
result = AUTORELEASE([[NSString alloc]
initWithCharactersNoCopy: output
length: outLength
freeWhenDone: YES]);
}
else
{
NSZoneFree(0, output);
}
}
uregex_close(r);
return result;
}
- (NSString*) replacementStringForResult: (NSTextCheckingResult*)result
@ -1030,7 +1127,7 @@ prepareResult(NSRegularExpression *regex,
uint32_t replLength = [template length];
unichar replacement[replLength];
int32_t outLength;
unichar *output;
NSString *str = nil;
TEMP_BUFFER(buffer, range.length);
r = setupRegex(regex,
@ -1043,12 +1140,28 @@ prepareResult(NSRegularExpression *regex,
[template getCharacters: replacement range: NSMakeRange(0, replLength)];
outLength = uregex_replaceFirst(r, replacement, replLength, NULL, 0, &s);
s = 0;
output = NSZoneMalloc(0, outLength * sizeof(unichar));
uregex_replaceFirst(r, replacement, replLength, output, outLength, &s);
return AUTORELEASE([[NSString alloc] initWithCharactersNoCopy: output
length: outLength
freeWhenDone: YES]);
if (0 == s || U_BUFFER_OVERFLOW_ERROR == s)
{
unichar *output;
s = 0;
output = NSZoneMalloc(0, (outLength + 1) * sizeof(unichar));
uregex_replaceFirst(r, replacement, replLength,
output, outLength + 1, &s);
if (0 == s)
{
str = AUTORELEASE([[NSString alloc]
initWithCharactersNoCopy: output
length: outLength
freeWhenDone: YES]);
}
else
{
NSZoneFree(0, output);
}
}
uregex_close(r);
return str;
}
#endif