plan9port/src/cmd/sam/rasp.c
Russ Cox 3ccd61629b sam: avoid out-of-bounds read in rterm
Usually r->nused < r->nalloc and the read is in bounds.
But it could in theory be right on the line and reading
past the end of the allocation.

Make it safe but preserve as much of the old semantics
as possible. This use of rterm appears to be only for
optimization purposes so the result does not matter
for correctness.
2021-01-14 10:05:50 -05:00

339 lines
5.7 KiB
C

#include "sam.h"
/*
* GROWDATASIZE must be big enough that all errors go out as Hgrowdata's,
* so they will be scrolled into visibility in the ~~sam~~ window (yuck!).
*/
#define GROWDATASIZE 50 /* if size is <= this, send data with grow */
void rcut(List*, Posn, Posn);
int rterm(List*, Posn);
void rgrow(List*, Posn, Posn);
static Posn growpos;
static Posn grown;
static Posn shrinkpos;
static Posn shrunk;
/*
* rasp routines inform the terminal of changes to the file.
*
* a rasp is a list of spans within the file, and an indication
* of whether the terminal knows about the span.
*
* optimize by coalescing multiple updates to the same span
* if it is not known by the terminal.
*
* other possible optimizations: flush terminal's rasp by cut everything,
* insert everything if rasp gets too large.
*/
/*
* only called for initial load of file
*/
void
raspload(File *f)
{
if(f->rasp == nil)
return;
grown = f->b.nc;
growpos = 0;
if(f->b.nc)
rgrow(f->rasp, 0, f->b.nc);
raspdone(f, 1);
}
void
raspstart(File *f)
{
if(f->rasp == nil)
return;
grown = 0;
shrunk = 0;
outbuffered = 1;
}
void
raspdone(File *f, int toterm)
{
if(f->dot.r.p1 > f->b.nc)
f->dot.r.p1 = f->b.nc;
if(f->dot.r.p2 > f->b.nc)
f->dot.r.p2 = f->b.nc;
if(f->mark.p1 > f->b.nc)
f->mark.p1 = f->b.nc;
if(f->mark.p2 > f->b.nc)
f->mark.p2 = f->b.nc;
if(f->rasp == nil)
return;
if(grown)
outTsll(Hgrow, f->tag, growpos, grown);
else if(shrunk)
outTsll(Hcut, f->tag, shrinkpos, shrunk);
if(toterm)
outTs(Hcheck0, f->tag);
outflush();
outbuffered = 0;
if(f == cmd){
cmdpt += cmdptadv;
cmdptadv = 0;
}
}
void
raspflush(File *f)
{
if(grown){
outTsll(Hgrow, f->tag, growpos, grown);
grown = 0;
}
else if(shrunk){
outTsll(Hcut, f->tag, shrinkpos, shrunk);
shrunk = 0;
}
outflush();
}
void
raspdelete(File *f, uint p1, uint p2, int toterm)
{
long n;
n = p2 - p1;
if(n == 0)
return;
if(p2 <= f->dot.r.p1){
f->dot.r.p1 -= n;
f->dot.r.p2 -= n;
}
if(p2 <= f->mark.p1){
f->mark.p1 -= n;
f->mark.p2 -= n;
}
if(f->rasp == nil)
return;
if(f==cmd && p1<cmdpt){
if(p2 <= cmdpt)
cmdpt -= n;
else
cmdpt = p1;
}
if(toterm){
if(grown){
outTsll(Hgrow, f->tag, growpos, grown);
grown = 0;
}else if(shrunk && shrinkpos!=p1 && shrinkpos!=p2){
outTsll(Hcut, f->tag, shrinkpos, shrunk);
shrunk = 0;
}
if(!shrunk || shrinkpos==p2)
shrinkpos = p1;
shrunk += n;
}
rcut(f->rasp, p1, p2);
}
void
raspinsert(File *f, uint p1, Rune *buf, uint n, int toterm)
{
Range r;
if(n == 0)
return;
if(p1 < f->dot.r.p1){
f->dot.r.p1 += n;
f->dot.r.p2 += n;
}
if(p1 < f->mark.p1){
f->mark.p1 += n;
f->mark.p2 += n;
}
if(f->rasp == nil)
return;
if(f==cmd && p1<cmdpt)
cmdpt += n;
if(toterm){
if(shrunk){
outTsll(Hcut, f->tag, shrinkpos, shrunk);
shrunk = 0;
}
if(n>GROWDATASIZE || !rterm(f->rasp, p1)){
rgrow(f->rasp, p1, n);
if(grown && growpos+grown!=p1 && growpos!=p1){
outTsll(Hgrow, f->tag, growpos, grown);
grown = 0;
}
if(!grown)
growpos = p1;
grown += n;
}else{
if(grown){
outTsll(Hgrow, f->tag, growpos, grown);
grown = 0;
}
rgrow(f->rasp, p1, n);
r = rdata(f->rasp, p1, n);
if(r.p1!=p1 || r.p2!=p1+n)
panic("rdata in toterminal");
outTsllS(Hgrowdata, f->tag, p1, n, tmprstr(buf, n));
}
}else{
rgrow(f->rasp, p1, n);
r = rdata(f->rasp, p1, n);
if(r.p1!=p1 || r.p2!=p1+n)
panic("rdata in toterminal");
}
}
#define M 0x80000000L
#define P(i) r->posnptr[i]
#define T(i) (P(i)&M) /* in terminal */
#define L(i) (P(i)&~M) /* length of this piece */
void
rcut(List *r, Posn p1, Posn p2)
{
Posn p, x;
int i;
if(p1 == p2)
panic("rcut 0");
for(p=0,i=0; i<r->nused && p+L(i)<=p1; p+=L(i++))
;
if(i == r->nused)
panic("rcut 1");
if(p < p1){ /* chop this piece */
if(p+L(i) < p2){
x = p1-p;
p += L(i);
}else{
x = L(i)-(p2-p1);
p = p2;
}
if(T(i))
P(i) = x|M;
else
P(i) = x;
i++;
}
while(i<r->nused && p+L(i)<=p2){
p += L(i);
dellist(r, i);
}
if(p < p2){
if(i == r->nused)
panic("rcut 2");
x = L(i)-(p2-p);
if(T(i))
P(i) = x|M;
else
P(i) = x;
}
/* can we merge i and i-1 ? */
if(i>0 && i<r->nused && T(i-1)==T(i)){
x = L(i-1)+L(i);
dellist(r, i--);
if(T(i))
P(i)=x|M;
else
P(i)=x;
}
}
void
rgrow(List *r, Posn p1, Posn n)
{
Posn p;
int i;
if(n == 0)
panic("rgrow 0");
for(p=0,i=0; i<r->nused && p+L(i)<=p1; p+=L(i++))
;
if(i == r->nused){ /* stick on end of file */
if(p!=p1)
panic("rgrow 1");
if(i>0 && !T(i-1))
P(i-1)+=n;
else
inslist(r, i, n);
}else if(!T(i)) /* goes in this empty piece */
P(i)+=n;
else if(p==p1 && i>0 && !T(i-1)) /* special case; simplifies life */
P(i-1)+=n;
else if(p==p1)
inslist(r, i, n);
else{ /* must break piece in terminal */
inslist(r, i+1, (L(i)-(p1-p))|M);
inslist(r, i+1, n);
P(i) = (p1-p)|M;
}
}
int
rterm(List *r, Posn p1)
{
Posn p;
int i;
for(p = 0,i = 0; i<r->nused && p+L(i)<=p1; p+=L(i++))
;
if(i==r->nused)
return i > 0 && T(i-1);
return T(i);
}
Range
rdata(List *r, Posn p1, Posn n)
{
Posn p;
int i;
Range rg;
if(n==0)
panic("rdata 0");
for(p = 0,i = 0; i<r->nused && p+L(i)<=p1; p+=L(i++))
;
if(i==r->nused)
panic("rdata 1");
if(T(i)){
n-=L(i)-(p1-p);
if(n<=0){
rg.p1 = rg.p2 = p1;
return rg;
}
p+=L(i++);
p1 = p;
}
if(T(i) || i==r->nused)
panic("rdata 2");
if(p+L(i)<p1+n)
n = L(i)-(p1-p);
rg.p1 = p1;
rg.p2 = p1+n;
if(p!=p1){
inslist(r, i+1, L(i)-(p1-p));
P(i)=p1-p;
i++;
}
if(L(i)!=n){
inslist(r, i+1, L(i)-n);
P(i)=n;
}
P(i)|=M;
/* now i is set; can we merge? */
if(i<r->nused-1 && T(i+1)){
P(i)=(n+=L(i+1))|M;
dellist(r, i+1);
}
if(i>0 && T(i-1)){
P(i)=(n+L(i-1))|M;
dellist(r, i-1);
}
return rg;
}