Skip to content

Commit fbb1dae

Browse files
committed
Fix incorrect addition, subtraction, and overflow checking in new
inet operators.
1 parent 77bb65d commit fbb1dae

File tree

4 files changed

+374
-263
lines changed

4 files changed

+374
-263
lines changed

src/backend/utils/adt/network.c

Lines changed: 66 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
/*
22
* PostgreSQL type definitions for the INET and CIDR types.
33
*
4-
* $PostgreSQL: pgsql/src/backend/utils/adt/network.c,v 1.64 2006/02/11 03:32:39 momjian Exp $
4+
* $PostgreSQL: pgsql/src/backend/utils/adt/network.c,v 1.65 2006/02/11 20:39:58 tgl Exp $
55
*
66
* Jon Postel RIP 16 Oct 1998
77
*/
@@ -27,7 +27,7 @@ static int32 network_cmp_internal(inet *a1, inet *a2);
2727
static int bitncmp(void *l, void *r, int n);
2828
static bool addressOK(unsigned char *a, int bits, int family);
2929
static int ip_addrsize(inet *inetptr);
30-
static Datum internal_inetpl(inet *ip, int64 iarg);
30+
static inet *internal_inetpl(inet *ip, int64 addend);
3131

3232
/*
3333
* Access macros.
@@ -1292,8 +1292,7 @@ inetand(PG_FUNCTION_ARGS)
12921292
if (ip_family(ip) != ip_family(ip2))
12931293
ereport(ERROR,
12941294
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
1295-
errmsg("mismatch in address family (%d) != (%d)",
1296-
ip_family(ip), ip_family(ip2))));
1295+
errmsg("cannot AND inet values of different sizes")));
12971296
else
12981297
{
12991298
int nb = ip_addrsize(ip);
@@ -1327,8 +1326,7 @@ inetor(PG_FUNCTION_ARGS)
13271326
if (ip_family(ip) != ip_family(ip2))
13281327
ereport(ERROR,
13291328
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
1330-
errmsg("mismatch in address family (%d) != (%d)",
1331-
ip_family(ip), ip_family(ip2))));
1329+
errmsg("cannot OR inet values of different sizes")));
13321330
else
13331331
{
13341332
int nb = ip_addrsize(ip);
@@ -1350,8 +1348,8 @@ inetor(PG_FUNCTION_ARGS)
13501348
}
13511349

13521350

1353-
static Datum
1354-
internal_inetpl(inet *ip, int64 plus)
1351+
static inet *
1352+
internal_inetpl(inet *ip, int64 addend)
13551353
{
13561354
inet *dst;
13571355

@@ -1365,15 +1363,31 @@ internal_inetpl(inet *ip, int64 plus)
13651363

13661364
while (nb-- > 0)
13671365
{
1368-
pdst[nb] = carry = pip[nb] + plus + carry;
1369-
plus /= 0x100; /* process next byte */
1370-
carry /= 0x100; /* remove low byte */
1371-
/* Overflow on high byte? */
1372-
if (nb == 0 && (plus != 0 || carry != 0))
1373-
ereport(ERROR,
1374-
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
1375-
errmsg("result out of range")));
1366+
carry = pip[nb] + (int) (addend & 0xFF) + carry;
1367+
pdst[nb] = (unsigned char) (carry & 0xFF);
1368+
carry >>= 8;
1369+
/*
1370+
* We have to be careful about right-shifting addend because
1371+
* right-shift isn't portable for negative values, while
1372+
* simply dividing by 256 doesn't work (the standard rounding
1373+
* is in the wrong direction, besides which there may be machines
1374+
* out there that round the wrong way). So, explicitly clear
1375+
* the low-order byte to remove any doubt about the correct
1376+
* result of the division, and then divide rather than shift.
1377+
*/
1378+
addend &= ~((int64) 0xFF);
1379+
addend /= 0x100;
13761380
}
1381+
/*
1382+
* At this point we should have addend and carry both zero if
1383+
* original addend was >= 0, or addend -1 and carry 1 if original
1384+
* addend was < 0. Anything else means overflow.
1385+
*/
1386+
if (!((addend == 0 && carry == 0) ||
1387+
(addend == -1 && carry == 1)))
1388+
ereport(ERROR,
1389+
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
1390+
errmsg("result out of range")));
13771391
}
13781392
ip_bits(dst) = ip_bits(ip);
13791393

@@ -1382,27 +1396,27 @@ internal_inetpl(inet *ip, int64 plus)
13821396
((char *) ip_addr(dst) - (char *) VARDATA(dst)) +
13831397
ip_addrsize(dst);
13841398

1385-
PG_RETURN_INET_P(dst);
1399+
return dst;
13861400
}
13871401

13881402

13891403
Datum
13901404
inetpl(PG_FUNCTION_ARGS)
13911405
{
13921406
inet *ip = PG_GETARG_INET_P(0);
1393-
int64 plus = PG_GETARG_INT64(1);
1407+
int64 addend = PG_GETARG_INT64(1);
13941408

1395-
return internal_inetpl(ip, plus);
1409+
PG_RETURN_INET_P(internal_inetpl(ip, addend));
13961410
}
13971411

13981412

13991413
Datum
14001414
inetmi_int8(PG_FUNCTION_ARGS)
14011415
{
14021416
inet *ip = PG_GETARG_INET_P(0);
1403-
int64 plus = PG_GETARG_INT64(1);
1417+
int64 addend = PG_GETARG_INT64(1);
14041418

1405-
return internal_inetpl(ip, -plus);
1419+
PG_RETURN_INET_P(internal_inetpl(ip, -addend));
14061420
}
14071421

14081422

@@ -1416,42 +1430,53 @@ inetmi(PG_FUNCTION_ARGS)
14161430
if (ip_family(ip) != ip_family(ip2))
14171431
ereport(ERROR,
14181432
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
1419-
errmsg("mismatch in address family (%d) != (%d)",
1420-
ip_family(ip), ip_family(ip2))));
1433+
errmsg("cannot subtract inet values of different sizes")));
14211434
else
14221435
{
1436+
/*
1437+
* We form the difference using the traditional complement,
1438+
* increment, and add rule, with the increment part being handled
1439+
* by starting the carry off at 1. If you don't think integer
1440+
* arithmetic is done in two's complement, too bad.
1441+
*/
14231442
int nb = ip_addrsize(ip);
14241443
int byte = 0;
14251444
unsigned char *pip = ip_addr(ip);
14261445
unsigned char *pip2 = ip_addr(ip2);
1446+
int carry = 1;
14271447

14281448
while (nb-- > 0)
14291449
{
1430-
/*
1431-
* Error if overflow on last byte. This test is tricky
1432-
* because if the subtraction == 128 and res is negative, or
1433-
* if subtraction == -128 and res is positive, the result
1434-
* would still fit in int64.
1435-
*/
1436-
if (byte + 1 == sizeof(int64) &&
1437-
(pip[nb] - pip2[nb] >= 128 + (res < 0) ||
1438-
pip[nb] - pip2[nb] <= -128 - (res > 0)))
1439-
ereport(ERROR,
1440-
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
1441-
errmsg("result out of range")));
1442-
if (byte >= sizeof(int64))
1450+
int lobyte;
1451+
1452+
carry = pip[nb] + (~pip2[nb] & 0xFF) + carry;
1453+
lobyte = carry & 0xFF;
1454+
if (byte < sizeof(int64))
14431455
{
1444-
/* Error if bytes beyond int64 length differ. */
1445-
if (pip[nb] != pip2[nb])
1456+
res |= ((int64) lobyte) << (byte * 8);
1457+
}
1458+
else
1459+
{
1460+
/*
1461+
* Input wider than int64: check for overflow. All bytes
1462+
* to the left of what will fit should be 0 or 0xFF,
1463+
* depending on sign of the now-complete result.
1464+
*/
1465+
if ((res < 0) ? (lobyte != 0xFF) : (lobyte != 0))
14461466
ereport(ERROR,
14471467
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
14481468
errmsg("result out of range")));
14491469
}
1450-
else
1451-
res += (int64)(pip[nb] - pip2[nb]) << (byte * 8);
1452-
1470+
carry >>= 8;
14531471
byte++;
14541472
}
1473+
1474+
/*
1475+
* If input is narrower than int64, overflow is not possible, but
1476+
* we have to do proper sign extension.
1477+
*/
1478+
if (carry == 0 && byte < sizeof(int64))
1479+
res |= ((int64) -1) << (byte * 8);
14551480
}
14561481

14571482
PG_RETURN_INT64(res);

src/include/catalog/pg_proc.h

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
* Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
88
* Portions Copyright (c) 1994, Regents of the University of California
99
*
10-
* $PostgreSQL: pgsql/src/include/catalog/pg_proc.h,v 1.395 2006/02/11 03:32:39 momjian Exp $
10+
* $PostgreSQL: pgsql/src/include/catalog/pg_proc.h,v 1.396 2006/02/11 20:39:58 tgl Exp $
1111
*
1212
* NOTES
1313
* The script catalog/genbki.sh reads this file and generates .bki
@@ -2297,7 +2297,7 @@ DESCR("bitwise or");
22972297
DATA(insert OID = 1675 ( bitxor PGNSP PGUID 12 f f t f i 2 1560 "1560 1560" _null_ _null_ _null_ bitxor - _null_ ));
22982298
DESCR("bitwise exclusive or");
22992299
DATA(insert OID = 1676 ( bitnot PGNSP PGUID 12 f f t f i 1 1560 "1560" _null_ _null_ _null_ bitnot - _null_ ));
2300-
DESCR("bitwise negation");
2300+
DESCR("bitwise not");
23012301
DATA(insert OID = 1677 ( bitshiftleft PGNSP PGUID 12 f f t f i 2 1560 "1560 23" _null_ _null_ _null_ bitshiftleft - _null_ ));
23022302
DESCR("bitwise left shift");
23032303
DATA(insert OID = 1678 ( bitshiftright PGNSP PGUID 12 f f t f i 2 1560 "1560 23" _null_ _null_ _null_ bitshiftright - _null_ ));
@@ -2423,28 +2423,28 @@ DATA(insert OID = 1715 ( cidr PGNSP PGUID 12 f f t f i 1 650 "869" _null_ _n
24232423
DESCR("coerce inet to cidr");
24242424

24252425
DATA(insert OID = 2196 ( inet_client_addr PGNSP PGUID 12 f f f f s 0 869 "" _null_ _null_ _null_ inet_client_addr - _null_ ));
2426-
DESCR("INET address of the client");
2426+
DESCR("inet address of the client");
24272427
DATA(insert OID = 2197 ( inet_client_port PGNSP PGUID 12 f f f f s 0 23 "" _null_ _null_ _null_ inet_client_port - _null_ ));
24282428
DESCR("client's port number for this connection");
24292429
DATA(insert OID = 2198 ( inet_server_addr PGNSP PGUID 12 f f f f s 0 869 "" _null_ _null_ _null_ inet_server_addr - _null_ ));
2430-
DESCR("INET address of the server");
2430+
DESCR("inet address of the server");
24312431
DATA(insert OID = 2199 ( inet_server_port PGNSP PGUID 12 f f f f s 0 23 "" _null_ _null_ _null_ inet_server_port - _null_ ));
24322432
DESCR("server's port number for this connection");
24332433

24342434
DATA(insert OID = 2627 ( inetnot PGNSP PGUID 12 f f t f i 1 869 "869" _null_ _null_ _null_ inetnot - _null_ ));
2435-
DESCR("binary NOT");
2435+
DESCR("bitwise not");
24362436
DATA(insert OID = 2628 ( inetand PGNSP PGUID 12 f f t f i 2 869 "869 869" _null_ _null_ _null_ inetand - _null_ ));
2437-
DESCR("binary AND");
2437+
DESCR("bitwise and");
24382438
DATA(insert OID = 2629 ( inetor PGNSP PGUID 12 f f t f i 2 869 "869 869" _null_ _null_ _null_ inetor - _null_ ));
2439-
DESCR("binary OR");
2439+
DESCR("bitwise or");
24402440
DATA(insert OID = 2630 ( inetpl PGNSP PGUID 12 f f t f i 2 869 "869 20" _null_ _null_ _null_ inetpl - _null_ ));
2441-
DESCR("add integer to INET value");
2442-
DATA(insert OID = 2631 ( int8pl_inet PGNSP PGUID 14 f f t f i 2 869 "20 869" _null_ _null_ _null_ "select $2 + $1" - _null_ ));
2443-
DESCR("add integer to INET value");
2441+
DESCR("add integer to inet value");
2442+
DATA(insert OID = 2631 ( int8pl_inet PGNSP PGUID 14 f f t f i 2 869 "20 869" _null_ _null_ _null_ "select $2 + $1" - _null_ ));
2443+
DESCR("add integer to inet value");
24442444
DATA(insert OID = 2632 ( inetmi_int8 PGNSP PGUID 12 f f t f i 2 869 "869 20" _null_ _null_ _null_ inetmi_int8 - _null_ ));
2445-
DESCR("subtract integer from INET value");
2445+
DESCR("subtract integer from inet value");
24462446
DATA(insert OID = 2633 ( inetmi PGNSP PGUID 12 f f t f i 2 20 "869 869" _null_ _null_ _null_ inetmi - _null_ ));
2447-
DESCR("subtract INET values");
2447+
DESCR("subtract inet values");
24482448

24492449
DATA(insert OID = 1686 ( numeric PGNSP PGUID 12 f f t f i 1 1700 "25" _null_ _null_ _null_ text_numeric - _null_ ));
24502450
DESCR("(internal)");

0 commit comments

Comments
 (0)