Fossil SCM

More work on making the xfer protocol fail fast for certain invalid inputs.

stephan 2024-09-24 11:13 trunk
Commit f9f90d9b15cb3669d803d5ebf4e0726cfc1c906cfa4bafe2b22ee0a4754e7afc
1 file changed +28 -6
+28 -6
--- src/xfer.c
+++ src/xfer.c
@@ -336,10 +336,11 @@
336336
|| !blob_is_filename(&pXfer->aToken[1])
337337
|| !blob_is_int64(&pXfer->aToken[2], &mtime)
338338
|| (!blob_eq(pHash,"-") && !blob_is_hname(pHash))
339339
|| !blob_is_int(&pXfer->aToken[4], &sz)
340340
|| !blob_is_int(&pXfer->aToken[5], &flags)
341
+ || (mtime<0 || sz<0 || flags<0)
341342
){
342343
blob_appendf(&pXfer->err, "malformed uvfile line");
343344
return;
344345
}
345346
blob_init(&content, 0, 0);
@@ -1122,19 +1123,20 @@
11221123
return db_get("xfer-ticket-script", 0);
11231124
}
11241125
11251126
/*
11261127
** Reset the CGI content, roll back any pending db transaction, and
1127
-** emit an "error" xfer message, which must be pre-fossilized by the
1128
-** caller.
1128
+** emit an "error" xfer message. The message text gets fossil-encoded
1129
+** by this function. This is only intended for use with
1130
+** fail-fast/fatal errors, not ones which can be skipped over.
11291131
*/
1130
-static void xfer_error(const char *zFossilizedMsg){
1132
+static void xfer_fatal_error(const char *zMsg){
11311133
cgi_reset_content();
1132
- if( db_transaction_nesting_depth() > 0 ){
1134
+ if( db_transaction_nesting_depth()>0 ){
11331135
db_rollback_transaction();
11341136
}
1135
- @ error %s(zFossilizedMsg)
1137
+ @ error %F(zMsg)
11361138
}
11371139
11381140
/*
11391141
** Run the specified TH1 script, if any, and returns 1 on error.
11401142
*/
@@ -1475,11 +1477,11 @@
14751477
if( iVers>=3 ){
14761478
cgi_set_content_type("application/x-fossil-uncompressed");
14771479
}
14781480
blob_is_int(&xfer.aToken[2], &seqno);
14791481
if( seqno<=0 ){
1480
- xfer_error("invalid\\sclone\\ssequence\\snumber");
1482
+ xfer_fatal_error("invalid clone sequence number");
14811483
return;
14821484
}
14831485
max = db_int(0, "SELECT max(rid) FROM blob");
14841486
while( xfer.mxSend>(int)blob_size(xfer.pOut) && seqno<=max){
14851487
if( time(NULL) >= xfer.maxTime ) break;
@@ -1549,10 +1551,14 @@
15491551
*/
15501552
if( blob_eq(&xfer.aToken[0],"config") && xfer.nToken==3
15511553
&& blob_is_int(&xfer.aToken[2], &size) ){
15521554
const char *zName = blob_str(&xfer.aToken[1]);
15531555
Blob content;
1556
+ if( size<0 ){
1557
+ xfer_fatal_error("invalid config record");
1558
+ return;
1559
+ }
15541560
blob_zero(&content);
15551561
blob_extract(xfer.pIn, size, &content);
15561562
if( !g.perm.Admin ){
15571563
cgi_reset_content();
15581564
@ error not\sauthorized\sto\spush\sconfiguration
@@ -2504,10 +2510,14 @@
25042510
&& (blob_eq(&xfer.aToken[3],"-") || blob_is_hname(&xfer.aToken[3]))
25052511
){
25062512
const char *zName = blob_str(&xfer.aToken[1]);
25072513
const char *zHash = blob_str(&xfer.aToken[3]);
25082514
int iStatus;
2515
+ if( mtime<0 || size<0 ){
2516
+ xfer_fatal_error("invalid uvigot");
2517
+ return ++nErr;
2518
+ }
25092519
iStatus = unversioned_status(zName, mtime, zHash);
25102520
if( (syncFlags & SYNC_UV_REVERT)!=0 ){
25112521
if( iStatus==4 ) iStatus = 2;
25122522
if( iStatus==5 ) iStatus = 1;
25132523
}
@@ -2590,10 +2600,14 @@
25902600
*/
25912601
if( blob_eq(&xfer.aToken[0],"config") && xfer.nToken==3
25922602
&& blob_is_int(&xfer.aToken[2], &size) ){
25932603
const char *zName = blob_str(&xfer.aToken[1]);
25942604
Blob content;
2605
+ if( size<0 ){
2606
+ xfer_fatal_error("invalid config record");
2607
+ return ++nErr;
2608
+ }
25952609
blob_zero(&content);
25962610
blob_extract(xfer.pIn, size, &content);
25972611
g.perm.Admin = g.perm.RdAddr = 1;
25982612
configure_receive(zName, &content, origConfigRcvMask);
25992613
nCardRcvd++;
@@ -2634,10 +2648,14 @@
26342648
** blob that needs to be sent. If N<=0 that indicates that all blobs
26352649
** have been sent.
26362650
*/
26372651
if( blob_eq(&xfer.aToken[0], "clone_seqno") && xfer.nToken==2 ){
26382652
blob_is_int(&xfer.aToken[1], &cloneSeqno);
2653
+ if( cloneSeqno<0 ){
2654
+ xfer_fatal_error("invalid clone_seqno");
2655
+ return ++nErr;
2656
+ }
26392657
}else
26402658
26412659
/* message MESSAGE
26422660
**
26432661
** A message is received from the server. Print it.
@@ -2711,10 +2729,14 @@
27112729
char *zUser = blob_terminate(&xfer.aToken[2]);
27122730
sqlite3_int64 mtime, iNow;
27132731
defossilize(zUser);
27142732
iNow = time(NULL);
27152733
if( blob_is_int64(&xfer.aToken[3], &mtime) && iNow>mtime ){
2734
+ if( mtime<0 ){
2735
+ xfer_fatal_error("invalid ci-lock-fail time");
2736
+ return ++nErr;
2737
+ }
27162738
iNow = time(NULL);
27172739
fossil_print("\nParent check-in locked by %s %s ago\n",
27182740
zUser, human_readable_age((iNow+1-mtime)/86400.0));
27192741
}else{
27202742
fossil_print("\nParent check-in locked by %s\n", zUser);
27212743
--- src/xfer.c
+++ src/xfer.c
@@ -336,10 +336,11 @@
336 || !blob_is_filename(&pXfer->aToken[1])
337 || !blob_is_int64(&pXfer->aToken[2], &mtime)
338 || (!blob_eq(pHash,"-") && !blob_is_hname(pHash))
339 || !blob_is_int(&pXfer->aToken[4], &sz)
340 || !blob_is_int(&pXfer->aToken[5], &flags)
 
341 ){
342 blob_appendf(&pXfer->err, "malformed uvfile line");
343 return;
344 }
345 blob_init(&content, 0, 0);
@@ -1122,19 +1123,20 @@
1122 return db_get("xfer-ticket-script", 0);
1123 }
1124
1125 /*
1126 ** Reset the CGI content, roll back any pending db transaction, and
1127 ** emit an "error" xfer message, which must be pre-fossilized by the
1128 ** caller.
 
1129 */
1130 static void xfer_error(const char *zFossilizedMsg){
1131 cgi_reset_content();
1132 if( db_transaction_nesting_depth() > 0 ){
1133 db_rollback_transaction();
1134 }
1135 @ error %s(zFossilizedMsg)
1136 }
1137
1138 /*
1139 ** Run the specified TH1 script, if any, and returns 1 on error.
1140 */
@@ -1475,11 +1477,11 @@
1475 if( iVers>=3 ){
1476 cgi_set_content_type("application/x-fossil-uncompressed");
1477 }
1478 blob_is_int(&xfer.aToken[2], &seqno);
1479 if( seqno<=0 ){
1480 xfer_error("invalid\\sclone\\ssequence\\snumber");
1481 return;
1482 }
1483 max = db_int(0, "SELECT max(rid) FROM blob");
1484 while( xfer.mxSend>(int)blob_size(xfer.pOut) && seqno<=max){
1485 if( time(NULL) >= xfer.maxTime ) break;
@@ -1549,10 +1551,14 @@
1549 */
1550 if( blob_eq(&xfer.aToken[0],"config") && xfer.nToken==3
1551 && blob_is_int(&xfer.aToken[2], &size) ){
1552 const char *zName = blob_str(&xfer.aToken[1]);
1553 Blob content;
 
 
 
 
1554 blob_zero(&content);
1555 blob_extract(xfer.pIn, size, &content);
1556 if( !g.perm.Admin ){
1557 cgi_reset_content();
1558 @ error not\sauthorized\sto\spush\sconfiguration
@@ -2504,10 +2510,14 @@
2504 && (blob_eq(&xfer.aToken[3],"-") || blob_is_hname(&xfer.aToken[3]))
2505 ){
2506 const char *zName = blob_str(&xfer.aToken[1]);
2507 const char *zHash = blob_str(&xfer.aToken[3]);
2508 int iStatus;
 
 
 
 
2509 iStatus = unversioned_status(zName, mtime, zHash);
2510 if( (syncFlags & SYNC_UV_REVERT)!=0 ){
2511 if( iStatus==4 ) iStatus = 2;
2512 if( iStatus==5 ) iStatus = 1;
2513 }
@@ -2590,10 +2600,14 @@
2590 */
2591 if( blob_eq(&xfer.aToken[0],"config") && xfer.nToken==3
2592 && blob_is_int(&xfer.aToken[2], &size) ){
2593 const char *zName = blob_str(&xfer.aToken[1]);
2594 Blob content;
 
 
 
 
2595 blob_zero(&content);
2596 blob_extract(xfer.pIn, size, &content);
2597 g.perm.Admin = g.perm.RdAddr = 1;
2598 configure_receive(zName, &content, origConfigRcvMask);
2599 nCardRcvd++;
@@ -2634,10 +2648,14 @@
2634 ** blob that needs to be sent. If N<=0 that indicates that all blobs
2635 ** have been sent.
2636 */
2637 if( blob_eq(&xfer.aToken[0], "clone_seqno") && xfer.nToken==2 ){
2638 blob_is_int(&xfer.aToken[1], &cloneSeqno);
 
 
 
 
2639 }else
2640
2641 /* message MESSAGE
2642 **
2643 ** A message is received from the server. Print it.
@@ -2711,10 +2729,14 @@
2711 char *zUser = blob_terminate(&xfer.aToken[2]);
2712 sqlite3_int64 mtime, iNow;
2713 defossilize(zUser);
2714 iNow = time(NULL);
2715 if( blob_is_int64(&xfer.aToken[3], &mtime) && iNow>mtime ){
 
 
 
 
2716 iNow = time(NULL);
2717 fossil_print("\nParent check-in locked by %s %s ago\n",
2718 zUser, human_readable_age((iNow+1-mtime)/86400.0));
2719 }else{
2720 fossil_print("\nParent check-in locked by %s\n", zUser);
2721
--- src/xfer.c
+++ src/xfer.c
@@ -336,10 +336,11 @@
336 || !blob_is_filename(&pXfer->aToken[1])
337 || !blob_is_int64(&pXfer->aToken[2], &mtime)
338 || (!blob_eq(pHash,"-") && !blob_is_hname(pHash))
339 || !blob_is_int(&pXfer->aToken[4], &sz)
340 || !blob_is_int(&pXfer->aToken[5], &flags)
341 || (mtime<0 || sz<0 || flags<0)
342 ){
343 blob_appendf(&pXfer->err, "malformed uvfile line");
344 return;
345 }
346 blob_init(&content, 0, 0);
@@ -1122,19 +1123,20 @@
1123 return db_get("xfer-ticket-script", 0);
1124 }
1125
1126 /*
1127 ** Reset the CGI content, roll back any pending db transaction, and
1128 ** emit an "error" xfer message. The message text gets fossil-encoded
1129 ** by this function. This is only intended for use with
1130 ** fail-fast/fatal errors, not ones which can be skipped over.
1131 */
1132 static void xfer_fatal_error(const char *zMsg){
1133 cgi_reset_content();
1134 if( db_transaction_nesting_depth()>0 ){
1135 db_rollback_transaction();
1136 }
1137 @ error %F(zMsg)
1138 }
1139
1140 /*
1141 ** Run the specified TH1 script, if any, and returns 1 on error.
1142 */
@@ -1475,11 +1477,11 @@
1477 if( iVers>=3 ){
1478 cgi_set_content_type("application/x-fossil-uncompressed");
1479 }
1480 blob_is_int(&xfer.aToken[2], &seqno);
1481 if( seqno<=0 ){
1482 xfer_fatal_error("invalid clone sequence number");
1483 return;
1484 }
1485 max = db_int(0, "SELECT max(rid) FROM blob");
1486 while( xfer.mxSend>(int)blob_size(xfer.pOut) && seqno<=max){
1487 if( time(NULL) >= xfer.maxTime ) break;
@@ -1549,10 +1551,14 @@
1551 */
1552 if( blob_eq(&xfer.aToken[0],"config") && xfer.nToken==3
1553 && blob_is_int(&xfer.aToken[2], &size) ){
1554 const char *zName = blob_str(&xfer.aToken[1]);
1555 Blob content;
1556 if( size<0 ){
1557 xfer_fatal_error("invalid config record");
1558 return;
1559 }
1560 blob_zero(&content);
1561 blob_extract(xfer.pIn, size, &content);
1562 if( !g.perm.Admin ){
1563 cgi_reset_content();
1564 @ error not\sauthorized\sto\spush\sconfiguration
@@ -2504,10 +2510,14 @@
2510 && (blob_eq(&xfer.aToken[3],"-") || blob_is_hname(&xfer.aToken[3]))
2511 ){
2512 const char *zName = blob_str(&xfer.aToken[1]);
2513 const char *zHash = blob_str(&xfer.aToken[3]);
2514 int iStatus;
2515 if( mtime<0 || size<0 ){
2516 xfer_fatal_error("invalid uvigot");
2517 return ++nErr;
2518 }
2519 iStatus = unversioned_status(zName, mtime, zHash);
2520 if( (syncFlags & SYNC_UV_REVERT)!=0 ){
2521 if( iStatus==4 ) iStatus = 2;
2522 if( iStatus==5 ) iStatus = 1;
2523 }
@@ -2590,10 +2600,14 @@
2600 */
2601 if( blob_eq(&xfer.aToken[0],"config") && xfer.nToken==3
2602 && blob_is_int(&xfer.aToken[2], &size) ){
2603 const char *zName = blob_str(&xfer.aToken[1]);
2604 Blob content;
2605 if( size<0 ){
2606 xfer_fatal_error("invalid config record");
2607 return ++nErr;
2608 }
2609 blob_zero(&content);
2610 blob_extract(xfer.pIn, size, &content);
2611 g.perm.Admin = g.perm.RdAddr = 1;
2612 configure_receive(zName, &content, origConfigRcvMask);
2613 nCardRcvd++;
@@ -2634,10 +2648,14 @@
2648 ** blob that needs to be sent. If N<=0 that indicates that all blobs
2649 ** have been sent.
2650 */
2651 if( blob_eq(&xfer.aToken[0], "clone_seqno") && xfer.nToken==2 ){
2652 blob_is_int(&xfer.aToken[1], &cloneSeqno);
2653 if( cloneSeqno<0 ){
2654 xfer_fatal_error("invalid clone_seqno");
2655 return ++nErr;
2656 }
2657 }else
2658
2659 /* message MESSAGE
2660 **
2661 ** A message is received from the server. Print it.
@@ -2711,10 +2729,14 @@
2729 char *zUser = blob_terminate(&xfer.aToken[2]);
2730 sqlite3_int64 mtime, iNow;
2731 defossilize(zUser);
2732 iNow = time(NULL);
2733 if( blob_is_int64(&xfer.aToken[3], &mtime) && iNow>mtime ){
2734 if( mtime<0 ){
2735 xfer_fatal_error("invalid ci-lock-fail time");
2736 return ++nErr;
2737 }
2738 iNow = time(NULL);
2739 fossil_print("\nParent check-in locked by %s %s ago\n",
2740 zUser, human_readable_age((iNow+1-mtime)/86400.0));
2741 }else{
2742 fossil_print("\nParent check-in locked by %s\n", zUser);
2743

Keyboard Shortcuts

Open search /
Next entry (timeline) j
Previous entry (timeline) k
Open focused entry Enter
Show this help ?
Toggle theme Top nav button