vfs: check userland buffers before reading them.
[haiku.git] / src / kits / app / LinkReceiver.cpp
blob40ddd888fdb675f6e4f6265971b690e1332395be
1 /*
2 * Copyright 2001-2011, Haiku.
3 * Distributed under the terms of the MIT License.
5 * Authors:
6 * Pahtz <pahtz@yahoo.com.au>
7 * Axel Dörfler
8 * Stephan Aßmus <superstippi@gmx.de>
9 * Artur Wyszynski <harakash@gmail.com>
13 /*! Class for low-overhead port-based messaging */
16 #include <LinkReceiver.h>
18 #include <stdlib.h>
19 #include <string.h>
20 #include <new>
22 #include <ServerProtocol.h>
23 #include <String.h>
24 #include <Region.h>
25 #include <GradientLinear.h>
26 #include <GradientRadial.h>
27 #include <GradientRadialFocus.h>
28 #include <GradientDiamond.h>
29 #include <GradientConic.h>
31 #include "link_message.h"
33 //#define DEBUG_BPORTLINK
34 #ifdef DEBUG_BPORTLINK
35 # include <stdio.h>
36 # define STRACE(x) printf x
37 #else
38 # define STRACE(x) ;
39 #endif
41 //#define TRACE_LINK_RECEIVER_GRADIENTS
42 #ifdef TRACE_LINK_RECEIVER_GRADIENTS
43 # include <OS.h>
44 # define GTRACE(x) debug_printf x
45 #else
46 # define GTRACE(x) ;
47 #endif
50 namespace BPrivate {
53 LinkReceiver::LinkReceiver(port_id port)
55 fReceivePort(port), fRecvBuffer(NULL), fRecvPosition(0), fRecvStart(0),
56 fRecvBufferSize(0), fDataSize(0),
57 fReplySize(0), fReadError(B_OK)
62 LinkReceiver::~LinkReceiver()
64 free(fRecvBuffer);
68 void
69 LinkReceiver::SetPort(port_id port)
71 fReceivePort = port;
75 status_t
76 LinkReceiver::GetNextMessage(int32 &code, bigtime_t timeout)
78 fReadError = B_OK;
80 int32 remaining = fDataSize - (fRecvStart + fReplySize);
81 STRACE(("info: LinkReceiver GetNextReply() reports %ld bytes remaining in buffer.\n", remaining));
83 // find the position of the next message header in the buffer
84 message_header *header;
85 if (remaining <= 0) {
86 status_t err = ReadFromPort(timeout);
87 if (err < B_OK)
88 return err;
89 remaining = fDataSize;
90 header = (message_header *)fRecvBuffer;
91 } else {
92 fRecvStart += fReplySize; // start of the next message
93 fRecvPosition = fRecvStart;
94 header = (message_header *)(fRecvBuffer + fRecvStart);
97 // check we have a well-formed message
98 if (remaining < (int32)sizeof(message_header)) {
99 // we don't have enough data for a complete header
100 STRACE(("error info: LinkReceiver remaining %ld bytes is less than header size.\n", remaining));
101 ResetBuffer();
102 return B_ERROR;
105 fReplySize = header->size;
106 if (fReplySize > remaining || fReplySize < (int32)sizeof(message_header)) {
107 STRACE(("error info: LinkReceiver message size of %ld bytes smaller than header size.\n", fReplySize));
108 ResetBuffer();
109 return B_ERROR;
112 code = header->code;
113 fRecvPosition += sizeof(message_header);
115 STRACE(("info: LinkReceiver got header %ld [%ld %ld %ld] from port %ld.\n",
116 header->code, fReplySize, header->code, header->flags, fReceivePort));
118 return B_OK;
122 bool
123 LinkReceiver::HasMessages() const
125 return fDataSize - (fRecvStart + fReplySize) > 0
126 || port_count(fReceivePort) > 0;
130 bool
131 LinkReceiver::NeedsReply() const
133 if (fReplySize == 0)
134 return false;
136 message_header *header = (message_header *)(fRecvBuffer + fRecvStart);
137 return (header->flags & kNeedsReply) != 0;
141 int32
142 LinkReceiver::Code() const
144 if (fReplySize == 0)
145 return B_ERROR;
147 message_header *header = (message_header *)(fRecvBuffer + fRecvStart);
148 return header->code;
152 void
153 LinkReceiver::ResetBuffer()
155 fRecvPosition = 0;
156 fRecvStart = 0;
157 fDataSize = 0;
158 fReplySize = 0;
162 status_t
163 LinkReceiver::AdjustReplyBuffer(bigtime_t timeout)
165 // Here we take advantage of the compiler's dead-code elimination
166 if (kInitialBufferSize == kMaxBufferSize) {
167 // fixed buffer size
169 if (fRecvBuffer != NULL)
170 return B_OK;
172 fRecvBuffer = (char *)malloc(kInitialBufferSize);
173 if (fRecvBuffer == NULL)
174 return B_NO_MEMORY;
176 fRecvBufferSize = kInitialBufferSize;
177 } else {
178 STRACE(("info: LinkReceiver getting port_buffer_size().\n"));
180 ssize_t bufferSize;
181 do {
182 bufferSize = port_buffer_size_etc(fReceivePort,
183 timeout == B_INFINITE_TIMEOUT ? 0 : B_RELATIVE_TIMEOUT,
184 timeout);
185 } while (bufferSize == B_INTERRUPTED);
187 STRACE(("info: LinkReceiver got port_buffer_size() = %ld.\n", bufferSize));
189 if (bufferSize < 0)
190 return (status_t)bufferSize;
192 // make sure our receive buffer is large enough
193 if (bufferSize > fRecvBufferSize) {
194 if (bufferSize <= (ssize_t)kInitialBufferSize)
195 bufferSize = (ssize_t)kInitialBufferSize;
196 else
197 bufferSize = (bufferSize + B_PAGE_SIZE - 1) & ~(B_PAGE_SIZE - 1);
199 if (bufferSize > (ssize_t)kMaxBufferSize)
200 return B_ERROR; // we can't continue
202 STRACE(("info: LinkReceiver setting receive buffersize to %ld.\n", bufferSize));
203 char *buffer = (char *)malloc(bufferSize);
204 if (buffer == NULL)
205 return B_NO_MEMORY;
207 free(fRecvBuffer);
208 fRecvBuffer = buffer;
209 fRecvBufferSize = bufferSize;
213 return B_OK;
217 status_t
218 LinkReceiver::ReadFromPort(bigtime_t timeout)
220 // we are here so it means we finished reading the buffer contents
221 ResetBuffer();
223 status_t err = AdjustReplyBuffer(timeout);
224 if (err < B_OK)
225 return err;
227 int32 code;
228 ssize_t bytesRead;
230 STRACE(("info: LinkReceiver reading port %ld.\n", fReceivePort));
231 while (true) {
232 if (timeout != B_INFINITE_TIMEOUT) {
233 do {
234 bytesRead = read_port_etc(fReceivePort, &code, fRecvBuffer,
235 fRecvBufferSize, B_TIMEOUT, timeout);
236 } while (bytesRead == B_INTERRUPTED);
237 } else {
238 do {
239 bytesRead = read_port(fReceivePort, &code, fRecvBuffer,
240 fRecvBufferSize);
241 } while (bytesRead == B_INTERRUPTED);
244 STRACE(("info: LinkReceiver read %ld bytes.\n", bytesRead));
245 if (bytesRead < B_OK)
246 return bytesRead;
248 // we just ignore incorrect messages, and don't bother our caller
250 if (code != kLinkCode) {
251 STRACE(("wrong port message %lx received.\n", code));
252 continue;
255 // port read seems to be valid
256 break;
259 fDataSize = bytesRead;
260 return B_OK;
264 status_t
265 LinkReceiver::Read(void *data, ssize_t passedSize)
267 // STRACE(("info: LinkReceiver Read()ing %ld bytes...\n", size));
268 ssize_t size = passedSize;
270 if (fReadError < B_OK)
271 return fReadError;
273 if (data == NULL || size < 1) {
274 fReadError = B_BAD_VALUE;
275 return B_BAD_VALUE;
278 if (fDataSize == 0 || fReplySize == 0)
279 return B_NO_INIT; // need to call GetNextReply() first
281 bool useArea = false;
282 if ((size_t)size >= kMaxBufferSize) {
283 useArea = true;
284 size = sizeof(area_id);
287 if (fRecvPosition + size > fRecvStart + fReplySize) {
288 // reading past the end of current message
289 fReadError = B_BAD_VALUE;
290 return B_BAD_VALUE;
293 if (useArea) {
294 area_id sourceArea;
295 memcpy((void*)&sourceArea, fRecvBuffer + fRecvPosition, size);
297 area_info areaInfo;
298 if (get_area_info(sourceArea, &areaInfo) < B_OK)
299 fReadError = B_BAD_VALUE;
301 if (fReadError >= B_OK) {
302 void* areaAddress = areaInfo.address;
304 if (areaAddress && sourceArea >= B_OK) {
305 memcpy(data, areaAddress, passedSize);
306 delete_area(sourceArea);
309 } else {
310 memcpy(data, fRecvBuffer + fRecvPosition, size);
312 fRecvPosition += size;
313 return fReadError;
317 status_t
318 LinkReceiver::ReadString(char** _string, size_t* _length)
320 int32 length = 0;
321 status_t status = Read<int32>(&length);
323 if (status < B_OK)
324 return status;
326 char *string;
327 if (length < 0) {
328 status = B_ERROR;
329 goto err;
332 string = (char *)malloc(length + 1);
333 if (string == NULL) {
334 status = B_NO_MEMORY;
335 goto err;
338 if (length > 0) {
339 status = Read(string, length);
340 if (status < B_OK) {
341 free(string);
342 return status;
346 // make sure the string is null terminated
347 string[length] = '\0';
349 if (_length)
350 *_length = length;
352 *_string = string;
354 return B_OK;
356 err:
357 fRecvPosition -= sizeof(int32);
358 // rewind the transaction
359 return status;
363 status_t
364 LinkReceiver::ReadString(BString &string, size_t* _length)
366 int32 length = 0;
367 status_t status = Read<int32>(&length);
369 if (status < B_OK)
370 return status;
372 if (length < 0) {
373 status = B_ERROR;
374 goto err;
377 if (length > 0) {
378 char* buffer = string.LockBuffer(length + 1);
379 if (buffer == NULL) {
380 status = B_NO_MEMORY;
381 goto err;
384 status = Read(buffer, length);
385 if (status < B_OK) {
386 string.UnlockBuffer();
387 goto err;
390 // make sure the string is null terminated
391 buffer[length] = '\0';
392 string.UnlockBuffer(length);
393 } else
394 string = "";
396 if (_length)
397 *_length = length;
399 return B_OK;
401 err:
402 fRecvPosition -= sizeof(int32);
403 // rewind the transaction
404 return status;
408 status_t
409 LinkReceiver::ReadString(char *buffer, size_t bufferLength)
411 int32 length = 0;
412 status_t status = Read<int32>(&length);
414 if (status < B_OK)
415 return status;
417 if (length >= (int32)bufferLength) {
418 status = B_BUFFER_OVERFLOW;
419 goto err;
422 if (length < 0) {
423 status = B_ERROR;
424 goto err;
427 if (length > 0) {
428 status = Read(buffer, length);
429 if (status < B_OK)
430 goto err;
433 // make sure the string is null terminated
434 buffer[length] = '\0';
435 return B_OK;
437 err:
438 fRecvPosition -= sizeof(int32);
439 // rewind the transaction
440 return status;
443 status_t
444 LinkReceiver::ReadRegion(BRegion* region)
446 status_t status = Read(&region->fCount, sizeof(int32));
447 if (status >= B_OK)
448 status = Read(&region->fBounds, sizeof(clipping_rect));
449 if (status >= B_OK) {
450 if (!region->_SetSize(region->fCount))
451 status = B_NO_MEMORY;
452 else {
453 status = Read(region->fData,
454 region->fCount * sizeof(clipping_rect));
456 if (status < B_OK)
457 region->MakeEmpty();
459 return status;
463 static BGradient*
464 gradient_for_type(BGradient::Type type)
466 switch (type) {
467 case BGradient::TYPE_LINEAR:
468 return new (std::nothrow) BGradientLinear();
469 case BGradient::TYPE_RADIAL:
470 return new (std::nothrow) BGradientRadial();
471 case BGradient::TYPE_RADIAL_FOCUS:
472 return new (std::nothrow) BGradientRadialFocus();
473 case BGradient::TYPE_DIAMOND:
474 return new (std::nothrow) BGradientDiamond();
475 case BGradient::TYPE_CONIC:
476 return new (std::nothrow) BGradientConic();
477 case BGradient::TYPE_NONE:
478 return new (std::nothrow) BGradient();
480 return NULL;
484 status_t
485 LinkReceiver::ReadGradient(BGradient** _gradient)
487 GTRACE(("LinkReceiver::ReadGradient\n"));
489 BGradient::Type gradientType;
490 int32 colorsCount;
491 Read(&gradientType, sizeof(BGradient::Type));
492 status_t status = Read(&colorsCount, sizeof(int32));
493 if (status != B_OK)
494 return status;
496 BGradient* gradient = gradient_for_type(gradientType);
497 if (!gradient)
498 return B_NO_MEMORY;
500 *_gradient = gradient;
502 if (colorsCount > 0) {
503 BGradient::ColorStop stop;
504 for (int i = 0; i < colorsCount; i++) {
505 if ((status = Read(&stop, sizeof(BGradient::ColorStop))) != B_OK)
506 return status;
507 if (!gradient->AddColorStop(stop, i))
508 return B_NO_MEMORY;
512 switch (gradientType) {
513 case BGradient::TYPE_LINEAR:
515 GTRACE(("LinkReceiver::ReadGradient> type == TYPE_LINEAR\n"));
516 BGradientLinear* linear = (BGradientLinear*)gradient;
517 BPoint start;
518 BPoint end;
519 Read(&start, sizeof(BPoint));
520 if ((status = Read(&end, sizeof(BPoint))) != B_OK)
521 return status;
522 linear->SetStart(start);
523 linear->SetEnd(end);
524 return B_OK;
526 case BGradient::TYPE_RADIAL:
528 GTRACE(("LinkReceiver::ReadGradient> type == TYPE_RADIAL\n"));
529 BGradientRadial* radial = (BGradientRadial*)gradient;
530 BPoint center;
531 float radius;
532 Read(&center, sizeof(BPoint));
533 if ((status = Read(&radius, sizeof(float))) != B_OK)
534 return status;
535 radial->SetCenter(center);
536 radial->SetRadius(radius);
537 return B_OK;
539 case BGradient::TYPE_RADIAL_FOCUS:
541 GTRACE(("LinkReceiver::ReadGradient> type == TYPE_RADIAL_FOCUS\n"));
542 BGradientRadialFocus* radialFocus =
543 (BGradientRadialFocus*)gradient;
544 BPoint center;
545 BPoint focal;
546 float radius;
547 Read(&center, sizeof(BPoint));
548 Read(&focal, sizeof(BPoint));
549 if ((status = Read(&radius, sizeof(float))) != B_OK)
550 return status;
551 radialFocus->SetCenter(center);
552 radialFocus->SetFocal(focal);
553 radialFocus->SetRadius(radius);
554 return B_OK;
556 case BGradient::TYPE_DIAMOND:
558 GTRACE(("LinkReceiver::ReadGradient> type == TYPE_DIAMOND\n"));
559 BGradientDiamond* diamond = (BGradientDiamond*)gradient;
560 BPoint center;
561 if ((status = Read(&center, sizeof(BPoint))) != B_OK)
562 return status;
563 diamond->SetCenter(center);
564 return B_OK;
566 case BGradient::TYPE_CONIC:
568 GTRACE(("LinkReceiver::ReadGradient> type == TYPE_CONIC\n"));
569 BGradientConic* conic = (BGradientConic*)gradient;
570 BPoint center;
571 float angle;
572 Read(&center, sizeof(BPoint));
573 if ((status = Read(&angle, sizeof(float))) != B_OK)
574 return status;
575 conic->SetCenter(center);
576 conic->SetAngle(angle);
577 return B_OK;
579 case BGradient::TYPE_NONE:
581 GTRACE(("LinkReceiver::ReadGradient> type == TYPE_NONE\n"));
582 break;
586 return B_ERROR;
590 } // namespace BPrivate