How to make (ptr + len) > lim safe?

M

Michael B Allen

I like to use limit pointers as sentinels when working on buffers
of data. Meaning I use ptr < lim where ptr and lim are both pointers
rather than n > 0 to determine if it is safe to proceed writing to that
location. The rationale is that it is simpler and thus safer because
you're not constantly recomputing integer n.

However, I've run into a case where this fails and I'd like to know what
the experts would do.

If I want to precompute if a pointer plus a length will exceed the limit
pointer I have a condition that can easily fail. There are two possible
expressions:

1) Add the length to the pointer and check to see if it exceeds the
limit like:

char *ptr, *lim;
int len;

if ((ptr + len) >= lim)
return -1;

This is not ok because len could be so large that the computed pointer
value becomes negative and the expression evaluates to false.

2) Compute the available space between the limit and pointer and compare
that to the required length:

if ((lim - ptr) =< len)
return -1;

This is also not ok because if lim is (char *)-1 and ptr is relatively
small, the computed space is still negative and thus the condition is
false. I'm not sure if this will happen on 32 bit platforms but on 64
bit it certainly can.

Note that I always also check to make sure lim != NULL and that ptr < lim.

So given any values for lim, ptr and len except lim != NULL and ptr <
lim, what expression would you use to safely ensure that ptr + len is
less than lim?

Mike
 
J

Jack Klein

I like to use limit pointers as sentinels when working on buffers
of data. Meaning I use ptr < lim where ptr and lim are both pointers
rather than n > 0 to determine if it is safe to proceed writing to that
location. The rationale is that it is simpler and thus safer because
you're not constantly recomputing integer n.

However, I've run into a case where this fails and I'd like to know what
the experts would do.

If I want to precompute if a pointer plus a length will exceed the limit
pointer I have a condition that can easily fail. There are two possible
expressions:

1) Add the length to the pointer and check to see if it exceeds the
limit like:

char *ptr, *lim;
int len;

The real problem here is that you are using the wrong integer type for
len. Use a size_t, which is the natural size for expressing object
sizes.

Even if you have to deal with a signed int type, assign it to a
size_t. If the signed int value is negative, it will convert to a
very large size_t value.

Then check the size_t value against a maximum value, which you decide
on. If and only if the size_t value (which is unsigned) is within
your limits should you apply it to the pointer.
if ((ptr + len) >= lim)
return -1;

This is not ok because len could be so large that the computed pointer
value becomes negative and the expression evaluates to false.

Pointers are not signed values, they don't come in "negative" and
"positive".
2) Compute the available space between the limit and pointer and compare
that to the required length:

if ((lim - ptr) =< len)
return -1;

This is also not ok because if lim is (char *)-1 and ptr is relatively
small, the computed space is still negative and thus the condition is
false. I'm not sure if this will happen on 32 bit platforms but on 64
bit it certainly can.

Note that I always also check to make sure lim != NULL and that ptr < lim.

So given any values for lim, ptr and len except lim != NULL and ptr <
lim, what expression would you use to safely ensure that ptr + len is
less than lim?

As I said above, my first step would be to convert the length to
size_t, an unsigned type, and check that against a predefined maximum.

--
Jack Klein
Home: http://JK-Technology.Com
FAQs for
comp.lang.c http://c-faq.com/
comp.lang.c++ http://www.parashift.com/c++-faq-lite/
alt.comp.lang.learn.c-c++
http://www.club.cc.cmu.edu/~ajo/docs/FAQ-acllc.html
 
J

James Kuyper

Michael said:
I like to use limit pointers as sentinels when working on buffers
of data. Meaning I use ptr < lim where ptr and lim are both pointers
rather than n > 0 to determine if it is safe to proceed writing to that
location. The rationale is that it is simpler and thus safer because
you're not constantly recomputing integer n.

However, I've run into a case where this fails and I'd like to know what
the experts would do.

If I want to precompute if a pointer plus a length will exceed the limit
pointer I have a condition that can easily fail. There are two possible
expressions: ....
2) Compute the available space between the limit and pointer and compare
that to the required length:

if ((lim - ptr) =< len)
return -1;

This is also not ok because if lim is (char *)-1 and ptr is relatively
small, the computed space is still negative and thus the condition is
false.

That's not guaranteed by the standard. The result of (char*)-1 "is
implementation-defined, might not be correctly aligned, might not point
to an entity of the referenced type, and might be a trap representation"
(6.3.2.3p5). The standard doesn't even attach any meaning to the concept
"ptr is relatively small".

It depends entirely upon your implementation whether those facts are
relevant to the question of whether or not lim-ptr is negative. The
standard says nothing from which you can draw any conclusions about it.

....
Note that I always also check to make sure lim != NULL and that ptr < lim.


However, if ptr and lim both point into or one past the end of the same
array, and if ptr < lim, then lim-ptr has to be positive, and that is
something the standard does say. Not directly, but by inference from
what it says about the binary '-' operator and the '<' operator, when
acting on pointers:

6.5.6p9: "When two pointers are subtracted, both shall point to elements
of the same array object, or one past the last element of the array
object; the result is the difference of the subscripts of the two array
elements."

6.5.8p5: "... pointers to array elements with larger subscript values
compare greater than pointers to elements of the same array with lower
subscript values."

Since ptr<lim, lim points to an array element with a larger subscript
value than the one ptr points at. The difference of the pointers is
equal to the difference of the subscripts, and therefore has to be positive.
 
P

Peter Nilsson

James Kuyper said:
However, if ptr and lim both point into or one past the end
of the same array, and if ptr < lim, then lim-ptr has to be
positive, and that is something the standard does say.

Chapter and verse please.
Not directly, but by inference from what it says about the
binary '-' operator and the '<' operator, when acting on
pointers:

6.5.6p9: "When two pointers are subtracted, both shall
point to elements of the same array object, or one past
the last element of the array object; the result is the
difference of the subscripts of the two array
elements."

6.5.8p5: "... pointers to array elements with larger
subscript values compare greater than pointers to
elements of the same array with lower subscript values."

Since ptr<lim, lim points to an array element with a
larger subscript value than the one ptr points at. The
difference of the pointers is equal to the difference
of the subscripts, and therefore has to be positive.

There is no requirement that PTRDIFF_MAX >= SIZE_MAX. So,
what is the value of (ptr + PTRDIFF_MAX + 1 - ptr) in the
case where the pointer additions are valid?
 
W

William Ahern

Michael B Allen said:
1) Add the length to the pointer and check to see if it exceeds the
limit like:
char *ptr, *lim;
int len;
if ((ptr + len) >= lim)
return -1;
This is not ok because len could be so large that the computed pointer
value becomes negative and the expression evaluates to false.

Where are you getting len? The scenario for me might be something like:

size_t fill(unsigned char *dst, size_t dstlen) {
unsigned char *pos = dst;
unsigned char *end = dst + dstlen;

while (pos < end) {
*(pos++) = '.';
}

return pos - dst;
}

Point being, if the caller hasn't written buggy code I can depend on the
pointer arithmetic being well defined. If dstlen is "too long" (or, if I
were using a signed integerr, negative), clearly the caller is passing
erroneous information about their output buffer, and there's nothing I can
do about that.

2) Compute the available space between the limit and pointer and compare
that to the required length:
if ((lim - ptr) =< len)
return -1;
This is also not ok because if lim is (char *)-1 and ptr is relatively
small, the computed space is still negative and thus the condition is
false. I'm not sure if this will happen on 32 bit platforms but on 64
bit it certainly can.

A negative pointer doesn't make sense. But, related to above, if lim was
calculated improperly, that's simply a bug, and one of a sort not restricted
to this type of usage.
Note that I always also check to make sure lim != NULL and that ptr < lim.

The former test isn't necessary. All that matters is if lim >= ptr and ptr <
lim (and that ptr points to a valid object). Testing for a NULL pointer is
[hopefully] superfluous and also an example of testing for the wrong
condition: it matters not that lim is NULL, but whether ptr is NULL. And in
any event, if both ptr and lim are NULL you're okay, as long as the
relationship holds.
So given any values for lim, ptr and len except lim != NULL and ptr <
lim, what expression would you use to safely ensure that ptr + len is
less than lim?

The real problem with using pointers this way is that, technically speaking,
you cannot do arbitrary pointer arthmetic this way if the calculations would
be undefined. On a segmented architecture if len is greater than the size of
the actual object, you get undefined behavior by computing and comparing
such a pointer. Likewise if you, as I often did, incremented a positional
pointer past [one past] the end of the object. In Unix these operations are
_better_ defined (though you obviously run into issues with overflow) and
usually more forgiving, because they make other requirements beyond the C
specification, such as a flat memory model. But I have run into debuggers
which have depended on the letter of the C specification, and it became
enough of a pain that I'm more careful about using pointers this way.

Instead, I'm increasingly more likely to use a position "pointer" which is
simply an integer counter, and instead of comparing pointers I compare the
counter with a length paramater. (Using unsigned arithmetic, of course.)
This way the counter can extend beyond the length, for instance if I want to
continuing calculating the output length of an operation, even if I've run
out of destination buffer space. (Cf snprintf). This may end up in slightly
additional CPU work, because I may need to recompute pointers more often (as
opposed to incrementing a pointer, which can also act as the counter). Or it
may not.
 
M

Michael B Allen

Pointers are not signed values, they don't come in "negative" and
"positive".

Yeah, I guess I meant to say it will "roll-over".

Perhaps an example is in order:

#include <stdlib.h>
#include <string.h>
#include <stdio.h>

int
copy_or_else(char *str, char *buf, char *blim)
{
size_t len = strlen(str) + 1;

if (buf == NULL || blim <= buf)
return -1;

if ((buf + len) >= blim)
return -1;

blim--;
while (*str && buf < blim) {
*buf++ = *str++;
}
*buf++ = '\0';

return 0;
}

int
main()
{
char buf[10], *blim;

blim = buf + sizeof(buf);
//blim = (void *)-1;

if (copy_or_else("hello world", buf, blim) < 0) {
fprintf(stderr, "Not enough buffer space.\n");
return EXIT_FAILURE;
}

printf("%s\n", buf);

return EXIT_SUCCESS;
}

Concerning buf and blim in copy_or_else only, is the above code legal?

I would like to be able to pass a blim of 'buf + sizeof(buf)' or some
value that means "no limit" which for which I have been using
'(void *)-1'. Note that blim is never dereferenced.

Now if len was somehow supplied by the caller then the "roll-over"
problem occurs in which case there's not a lot I can do about it because
copy_or_else doesn't know the size of buf?

What about:

if ((buf + len) < buf || (buf + len) >= blim)
return -1; // roll-over or passed blim

Mike
 
R

Richard Tobin

Michael B Allen said:
1) Add the length to the pointer and check to see if it exceeds the
limit like:

char *ptr, *lim;
int len;

if ((ptr + len) >= lim)
return -1;

There's no portable way to do this. You just can't safely computer
ptr+len if if might be outside the object (except for the special case
of one-beyond-the-end).

In practice, if you have "flat" pointers, you can do tests like this,
but because of wraparound you would need to compare against the lower
limit as well.

But the whole idea is misguided. You should not be relying on this
sort of check. After all, if people are handing you bogus pointers,
how can you be sure that the ones in range are correct?

-- Richard
 
P

pete

Michael B Allen wrote:
Perhaps an example is in order:

#include <stdlib.h>
#include <string.h>
#include <stdio.h>

int
copy_or_else(char *str, char *buf, char *blim)
{
size_t len = strlen(str) + 1;

if (buf == NULL || blim <= buf)
return -1;

if ((buf + len) >= blim)
return -1;

blim--;
while (*str && buf < blim) {
*buf++ = *str++;
}
*buf++ = '\0';

return 0;
}

int
main()
{
char buf[10], *blim;

blim = buf + sizeof(buf);
//blim = (void *)-1;

if (copy_or_else("hello world", buf, blim) < 0) {
fprintf(stderr, "Not enough buffer space.\n");
return EXIT_FAILURE;
}

printf("%s\n", buf);

return EXIT_SUCCESS;
}

Concerning buf and blim in copy_or_else only, is the above code legal?

No.
If (blim < buf) then the program isn't defined either.

Object pointers can only have 4 kinds of values:
1 object address
2 one past object address
3 null
4 indeterminate

Relational operators are only defined for pointers
which point to the same object, or one past.

/* BEGIN new.c */

#include <string.h>
#include <stdio.h>

#define STRING "hello from buf2."

int
copy_or_else(char *str, char *buf, size_t buff_size)
{
size_t size = strlen(str) + 1;
char *after = str + size;

if (size > buff_size) {
return -1;
}
do {
*buf++ = *str++;
} while (str != after);
return 0;
}

int
main()
{
char buf[10];
char buf2[sizeof STRING];

if (copy_or_else("hello world", buf, sizeof(buf)) < 0) {
fprintf(stderr, "Not enough buffer space in buf.\n");
} else {
puts(buf);
}

if (copy_or_else(STRING, buf2, sizeof(buf2)) < 0) {
fprintf(stderr, "Not enough buffer space in buf2.\n");
} else {
puts(buf2);
}

return 0;
}

/* END new.c */
 
J

James Kuyper

Peter said:
Chapter and verse please.

Already provided:
There is no requirement that PTRDIFF_MAX >= SIZE_MAX. So,
what is the value of (ptr + PTRDIFF_MAX + 1 - ptr) in the
case where the pointer additions are valid?

It is a negative value, and therefore a value that an implementation
should never permit to be the result of subtracting two pointers into
the same array, if the left operand compares greater than the right
operand. ptrdiff_t must be chosen to be large enough to ensure the
behavior required by 6.5.6p9 and 6.5.8p5.

Subscripts into an array are only allowed to be positive, regardless of
the value of PTRDIFF_MAX, and therefore the difference between a larger
subscript and a smaller one can never be negative. The description of
the binary '-' operator acting on pointers doesn't say that the result
is "the difference of the subscripts, after conversion to ptrdiff_t". It
just says that it's the difference of the subscripts.
 
M

Michael B Allen

No.
If (blim < buf) then the program isn't defined either.

Object pointers can only have 4 kinds of values:
1 object address
2 one past object address
3 null
4 indeterminate

Relational operators
are only defined for pointers
which point to the same object, or one past.

Hi Pete,

So what you're saying is that if someone can supply an invalid value
wrt to the standard, we should make no attempt to check for it?

If the user supplies a blim pointer that points to an element of buf
(or one after the end of buf) then is the above code legal?

If yes, then the means and end are the same so why NOT at least make an
attempt to check possible indeterminate values?

It seems to me that what you're saying only reenforces the idea of using
a limit pointer because it gives the function an opportunity to validate
the value.

Mike
 
C

CBFalconer

James said:
Peter Nilsson wrote:
.... snip ...


It is a negative value, and therefore a value that an
implementation should never permit to be the result of
subtracting two pointers into the same array, if the left
operand compares greater than the right operand. ptrdiff_t must
be chosen to be large enough to ensure the behavior required by
6.5.6p9 and 6.5.8p5.

Subscripts into an array are only allowed to be positive,
regardless of the value of PTRDIFF_MAX, and therefore the
difference between a larger subscript and a smaller one can
never be negative. The description of the binary '-' operator
acting on pointers doesn't say that the result is "the
difference of the subscripts, after conversion to ptrdiff_t".
It just says that it's the difference of the subscripts.

Not so. See section 6.5.6 of the standard, and the following:

[#9] When two pointers are subtracted, both shall point to
elements of the same array object, or one past the last
element of the array object; the result is the difference of
the subscripts of the two array elements. The size of the
result is implementation-defined, and its type (a signed
integer type) is ptrdiff_t defined in the <stddef.h> header.
If the result is not representable in an object of that
type, the behavior is undefined. In other words, if the
expressions P and Q point to, respectively, the i-th and j-
th elements of an array object, the expression (P)-(Q) has
the value i-j provided the value fits in an object of type
ptrdiff_t. Moreover, if the expression P points either to
an element of an array object or one past the last element
of an array object, and the expression Q points to the last
element of the same array object, the expression ((Q)+1)-(P)
has the same value as ((Q)-(P))+1 and as -((P)-((Q)+1)), and
has the value zero if the expression P points one past the
last element of the array object, even though the expression
(Q)+1 does not point to an element of the array object.79)

____________________

79)Another way to approach pointer arithmetic is first to
convert the pointer(s) to character pointer(s): In this
scheme the integer expression added to or subtracted from
the converted pointer is first multiplied by the size of
the object originally pointed to, and the resulting
pointer is converted back to the original type. For
pointer subtraction, the result of the difference between
the character pointers is similarly divided by the size
of the object originally pointed to.
When viewed in this way, an implementation need only
provide one extra byte (which may overlap another object
in the program) just after the end of the object in order
to satisfy the ``one past the last element''
requirements.
 
P

Paul Hsieh

I like to use limit pointers as sentinels when working on buffers
of data. Meaning I use ptr < lim where ptr and lim are both pointers
rather than n > 0 to determine if it is safe to proceed writing to
that location. The rationale is that it is simpler and thus safer
because you're not constantly recomputing integer n.

However, I've run into a case where this fails and I'd like to know
what the experts would do.

If I want to precompute if a pointer plus a length will exceed the
limit pointer I have a condition that can easily fail. There are two
possible expressions:

1) Add the length to the pointer and check to see if it exceeds the
limit like:

char *ptr, *lim;
int len;

if ((ptr + len) >= lim)
return -1;

This is not ok because len could be so large that the computed
pointer value becomes negative and the expression evaluates to false.

2) Compute the available space between the limit and pointer and
compare that to the required length:

if ((lim - ptr) =< len)
return -1;

This is also not ok because if lim is (char *)-1 and ptr is
relatively small, the computed space is still negative and thus the
condition is false. I'm not sure if this will happen on 32 bit
platforms but on 64 bit it certainly can.

Note that I always also check to make sure lim != NULL and that ptr <
lim.

So given any values for lim, ptr and len except lim != NULL and ptr <
lim, what expression would you use to safely ensure that ptr + len is
less than lim?

It can't be done perfectly in a single comparison. Assuming wrap
around semantics for pointer values and sizeof(int) <= sizeof
(intptr_t) what you want is:

intptr_t diff = (intptr_t) (lim - ptr);
if (ptr < lim) {
if (diff > 0 && len >= diff) return -1;
} else {
if (diff <= 0 && len >= diff) return -1;
}

The point being that len is a signed integer, and *cannot* cause ptr
+len to exceed the limit if lim is more than INTPTR_MAX away from ptr.

However, it is extremely unlikely that you need to actually do the
full comparison as shown above. In context of your code, you can
usually assume that ptr < lim, and depending on the size of your
object, you might be able to assume that |lim-ptr| < INTPTR_MAX. If
not, then you can usually determine the point at which some of the
conditions are already met (or not) by how ptr or len are being
modified in the code that precedes this point.

Personally, I have found that using signed integer offsets and
detecting the wrap around cases at the moment they might happen is the
best approach. So I compare offsets, and make sure I can successfully
assume that there are no wrap around effects by the time I am doing
limit checks ("The Better String Library" is based pervasively on this
-- extensive tests on 16 bit platforms (which I can cause overflows to
happen easily even though real memory is available to create such
strings) have been done to check that it is correct.)

You are also technically potentially violating the standard if you are
comparing pointers that are not referencing the same object. But if
you can assume that its the same object, then the assumptions listed
above can usually be made, and you can find an equivalence in
operations to the offset approach that I use (though, IMHO, its harder
to see if your code is correct).
 
P

pete

Michael said:
Hi Pete,

So what you're saying is that if someone can supply an invalid value
wrt to the standard, we should make no attempt to check for it?

Think etymologically.
Should there be any way to "determine"
the value of an "indeterminate" pointer?

You knew that you were taking a chance
and that you might have to learn something
as a consequence of posting your code,
so keep a stiff upper lip.

If (buf) is the address
of the lowest addressable byte of an object,
then the expression (buf - 1) is undefined.
The attempted evaluation of an undefined expression like
(buf > (buf - 1))
makes the entire c program undefined.
That's the wrong way to write code.
We aggressively discourage
the use of undefined code on this newsgroup.
 
M

Michael B Allen

If (buf) is the address
of the lowest addressable byte of an object,
then the expression (buf - 1) is undefined.
The attempted evaluation of an undefined expression like
(buf > (buf - 1))
makes the entire c program undefined.

You still have not answered why an invalid blim pointer is any different
from an invalid size_t size.

Both forms can compute invalid addresses of buf which means they both
exhibit behavior that is equally undefined.

However, your new.c function dereferences that address which in practice
is much more likey to cause a fault. Therefore you have yet again provided
a positive argument for using a limit pointer instead of a size_t.

Thanks,
Mike
 
P

Peter Nilsson

James Kuyper said:
Already provided:

It then says...

The size of the result is implementation-defined, and its
type (a signed integer type) is ptrdiff_t defined in the
<stddef.h> header. If the result is not representable in
an object of that type, the behavior is undefined. In
other words, if the expressions P and Q point to,
respectively, the i-th and j-th elements of an array
object, the expression (P)-(Q) has the value i-j provided
the value fits in an object of type ptrdiff_t. ...

Seems pretty clear to me that there's a possibility that
ptrdiff_t need not be able to represent a difference of two
otherwise valid pointers to elements from the same array.

ptrdiff_t must be chosen to be large enough to ensure the
behavior required by 6.5.6p9 and 6.5.8p5.

6.5.6p9 is pretty clear that ptrdiff_t need not be large
enough.
 
J

James Kuyper

Peter Nilsson wrote:
....
[Re: 6.5.6p9]
It then says...

The size of the result is implementation-defined, and its
type (a signed integer type) is ptrdiff_t defined in the
<stddef.h> header. If the result is not representable in
an object of that type, the behavior is undefined. In
other words, if the expressions P and Q point to,
respectively, the i-th and j-th elements of an array
object, the expression (P)-(Q) has the value i-j provided
the value fits in an object of type ptrdiff_t. ...

Seems pretty clear to me that there's a possibility that
ptrdiff_t need not be able to represent a difference of two
otherwise valid pointers to elements from the same array.

Conceded - I'd forgotten that part, and didn't happen to re-read it
while researching my response - I knew what I was looking for, and
stopped reading as soon as I found it.
 
R

Richard Tobin

Think etymologically.
Should there be any way to "determine"
the value of an "indeterminate" pointer?

Etymologically, that's exactly what you can do to one.

-- Richard
 
P

pete

Michael said:
You still have not answered why an
invalid blim pointer is any different
from an invalid size_t size.

Relational operations are defined between any two size_t values.
Relational operations are defined only between pointer values
that point to the same object or one past.

I may have been wrong about you code not being OK.

I don't understand under what circumstance
you think that (blim) might be less than (buf)
as is suggested by:

if (buf == NULL || blim <= buf)
return -1;

What condition is supposed to be indicated by (blim < buf)?
 
M

Michael B Allen

Relational operations are defined between any two size_t values.
Relational operations are defined only between pointer values
that point to the same object or one past.

I may have been wrong about you code not being OK.

I don't understand under what circumstance
you think that (blim) might be less than (buf)
as is suggested by:

if (buf == NULL || blim <= buf)
return -1;

What condition is supposed to be indicated by (blim < buf)?

If the function is use properly blim should not be less than buf.

However, I find that if someone can do something they eventually will. So
why not check? If they're using the function incorrectly, then undefined
behavior is moot anyway.

So to check the buffer parameters I need to check if buf == NULL or
if blim == NULL or if blim <= buf but I don't need to check if blim ==
NULL since blim <= buf would detect if blim == NULL.

Then, within the logic of the function I check buf <= blim as necessary
to make sure I don't write past the end (again assuming blim is valid).

If I understand what you're saying then there is no violation of the
standard if the buf and blim parameters are valid just as would be the
case if I used size_t.

Mike
 
W

William Ahern

Michael B Allen said:
If the function is use properly blim should not be less than buf.

However, I find that if someone can do something they eventually will. So
why not check? If they're using the function incorrectly, then undefined
behavior is moot anyway.

No, it's not. What the standard may leave undefined may nonetheless behave
in a constructive manner on the implementation.
So to check the buffer parameters I need to check if buf == NULL or
if blim == NULL or if blim <= buf but I don't need to check if blim ==
NULL since blim <= buf would detect if blim == NULL.

Then, within the logic of the function I check buf <= blim as necessary
to make sure I don't write past the end (again assuming blim is valid).

So what if buf was NULL? Good. Write to it. On many implementations this
will cause the application to crash, and the programmer will learn of his
mistake.

Likewise, if the caller has you writing past the end up a buffer, good!
There are implementations and/or debuggers which can catch this. Your
defenses measure may serve only to frustrate these tools, and to obsfuscate
or hide bugs.

Point being, these checks are often times counter-productive. They hide
bugs. I don't want my bugs hidden. Rather, I want to find them as soon as
possible.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,744
Messages
2,569,483
Members
44,903
Latest member
orderPeak8CBDGummies

Latest Threads

Top