summaryrefslogtreecommitdiff
path: root/src/gallium/drivers
diff options
context:
space:
mode:
authorJosé Fonseca <jfonseca@vmware.com>2010-01-27 15:41:25 +0000
committerJosé Fonseca <jfonseca@vmware.com>2010-02-22 21:46:09 +0000
commit422167a9b0dc5b6da7bc7c47621b5da1bc6d315b (patch)
tree2bbc72ddb6eaacc8751d393f47b94122e5a6f338 /src/gallium/drivers
parent12d5203bb39a6146c5fd08b66f49b4fc1feb1162 (diff)
svga: Prevent buffer overflow in buffer ranges.
Do this by extending the nearest range to cover the new range. This fixes an access fault in Call of Duty which was doing many disjoint glBufferSubData calls.
Diffstat (limited to 'src/gallium/drivers')
-rw-r--r--src/gallium/drivers/svga/svga_screen_buffer.c62
1 files changed, 54 insertions, 8 deletions
diff --git a/src/gallium/drivers/svga/svga_screen_buffer.c b/src/gallium/drivers/svga/svga_screen_buffer.c
index c9e9bef540..0371ddde36 100644
--- a/src/gallium/drivers/svga/svga_screen_buffer.c
+++ b/src/gallium/drivers/svga/svga_screen_buffer.c
@@ -311,10 +311,20 @@ svga_buffer_upload_queue(struct svga_buffer *sbuf,
unsigned end)
{
unsigned i;
+ unsigned nearest_range;
+ unsigned nearest_dist;
assert(sbuf->hw.buf);
assert(end > start);
+ if (sbuf->hw.num_ranges < SVGA_BUFFER_MAX_RANGES) {
+ nearest_range = sbuf->hw.num_ranges;
+ nearest_dist = ~0;
+ } else {
+ nearest_range = SVGA_BUFFER_MAX_RANGES - 1;
+ nearest_dist = 0;
+ }
+
/*
* Try to grow one of the ranges.
*
@@ -326,11 +336,33 @@ svga_buffer_upload_queue(struct svga_buffer *sbuf,
*/
for(i = 0; i < sbuf->hw.num_ranges; ++i) {
- if(start <= sbuf->hw.ranges[i].end && sbuf->hw.ranges[i].start <= end) {
+ int left_dist;
+ int right_dist;
+ int dist;
+
+ left_dist = start - sbuf->hw.ranges[i].end;
+ right_dist = sbuf->hw.ranges[i].start - end;
+ dist = MAX2(left_dist, right_dist);
+
+ if (dist <= 0) {
+ /*
+ * Ranges are contiguous or overlapping -- extend this one and return.
+ */
+
sbuf->hw.ranges[i].start = MIN2(sbuf->hw.ranges[i].start, start);
- sbuf->hw.ranges[i].end = MAX2(sbuf->hw.ranges[i].end, end);
+ sbuf->hw.ranges[i].end = MAX2(sbuf->hw.ranges[i].end, end);
return;
}
+ else {
+ /*
+ * Discontiguous ranges -- keep track of the nearest range.
+ */
+
+ if (dist < nearest_dist) {
+ nearest_range = i;
+ nearest_dist = dist;
+ }
+ }
}
/*
@@ -345,13 +377,27 @@ svga_buffer_upload_queue(struct svga_buffer *sbuf,
assert(!sbuf->hw.svga);
assert(!sbuf->hw.boxes);
- /*
- * Add a new range.
- */
+ if (sbuf->hw.num_ranges < SVGA_BUFFER_MAX_RANGES) {
+ /*
+ * Add a new range.
+ */
- sbuf->hw.ranges[sbuf->hw.num_ranges].start = start;
- sbuf->hw.ranges[sbuf->hw.num_ranges].end = end;
- ++sbuf->hw.num_ranges;
+ sbuf->hw.ranges[sbuf->hw.num_ranges].start = start;
+ sbuf->hw.ranges[sbuf->hw.num_ranges].end = end;
+ ++sbuf->hw.num_ranges;
+ } else {
+ /*
+ * Everything else failed, so just extend the nearest range.
+ *
+ * It is OK to do this because we always keep a local copy of the
+ * host buffer data, for SW TNL, and the host never modifies the buffer.
+ */
+
+ assert(nearest_range < SVGA_BUFFER_MAX_RANGES);
+ assert(nearest_range < sbuf->hw.num_ranges);
+ sbuf->hw.ranges[nearest_range].start = MIN2(sbuf->hw.ranges[nearest_range].start, start);
+ sbuf->hw.ranges[nearest_range].end = MAX2(sbuf->hw.ranges[nearest_range].end, end);
+ }
}