Skip to content

Commit cf9c903

Browse files
committed
trying another approach, checking max/min costs.
1 parent a8d1955 commit cf9c903

File tree

2 files changed

+72
-0
lines changed

2 files changed

+72
-0
lines changed

ext/standard/levenshtein.c

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ static zend_long reference_levdist(const zend_string *string1, const zend_string
3232
return ZSTR_LEN(string1) * cost_del;
3333
}
3434

35+
3536
p1 = safe_emalloc((ZSTR_LEN(string2) + 1), sizeof(zend_long), 0);
3637
p2 = safe_emalloc((ZSTR_LEN(string2) + 1), sizeof(zend_long), 0);
3738

@@ -78,6 +79,39 @@ PHP_FUNCTION(levenshtein)
7879
RETURN_THROWS();
7980
}
8081

82+
if (ZSTR_LEN(string1) > 0 || ZSTR_LEN(string2) > 0) {
83+
zend_long lc = MAX(cost_ins, MAX(cost_rep, cost_del));
84+
zend_long ml = MAX(ZSTR_LEN(string1), ZSTR_LEN(string2));
85+
zend_long ovl = ZEND_LONG_MAX / ml;
86+
87+
88+
if (lc > ovl) {
89+
int arg = 3;
90+
if (lc == cost_rep) {
91+
arg = 4;
92+
} else if (lc == cost_del) {
93+
arg = 5;
94+
}
95+
96+
zend_argument_value_error(arg, "must be at most " ZEND_LONG_FMT, ovl);
97+
RETURN_THROWS();
98+
}
99+
100+
zend_long mc = MIN(cost_ins, MIN(cost_rep, cost_del));
101+
zend_long uvl = ZEND_LONG_MIN / ml;
102+
103+
if (mc < uvl) {
104+
int arg = 3;
105+
if (mc == cost_rep) {
106+
arg = 4;
107+
} else if (mc == cost_del) {
108+
arg = 5;
109+
}
110+
111+
zend_argument_value_error(arg, "must be at least " ZEND_LONG_FMT, uvl);
112+
RETURN_THROWS();
113+
}
114+
}
81115

82116
RETURN_LONG(reference_levdist(string1, string2, cost_ins, cost_rep, cost_del));
83117
}
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
--TEST--
2+
GH-14807 overflow on insertion_cost/replacement_cost/deletion_cost
3+
--SKIPIF--
4+
<?php
5+
if (PHP_INT_SIZE != 8) die("skip this test is for 64bit platform only");
6+
?>
7+
--FILE--
8+
<?php
9+
$str1 = "abcd";
10+
$str2 = "defg";
11+
12+
foreach([PHP_INT_MIN, PHP_INT_MAX] as $lim) {
13+
try {
14+
levenshtein($str1, $str2, $lim);
15+
} catch (\ValueError $e) {
16+
echo $e->getMessage() . PHP_EOL;
17+
}
18+
19+
try {
20+
levenshtein($str1, $str2, 1, $lim);
21+
} catch (\ValueError $e) {
22+
echo $e->getMessage() . PHP_EOL;
23+
}
24+
25+
try {
26+
levenshtein($str1, $str2, 1, 1, $lim);
27+
} catch (\ValueError $e) {
28+
echo $e->getMessage() . PHP_EOL;
29+
}
30+
}
31+
?>
32+
--EXPECTF--
33+
levenshtein(): Argument #3 ($insertion_cost) must be at least %s
34+
levenshtein(): Argument #4 ($replacement_cost) must be at least %s
35+
levenshtein(): Argument #5 ($deletion_cost) must be at least %s
36+
levenshtein(): Argument #3 ($insertion_cost) must be at most %s
37+
levenshtein(): Argument #4 ($replacement_cost) must be at most %s
38+
levenshtein(): Argument #5 ($deletion_cost) must be at most %s

0 commit comments

Comments
 (0)