Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

[Q:H3] Strange behavior of the NULL pointer

20 views
Skip to first unread message

Michael_Huehne

unread,
Nov 5, 1994, 1:48:35 PM11/5/94
to
Hello there,

I've got to port a large (200000+ lines of code) program to Solaris
2.3. However, the program makes use of a strange feature of, for
example, SCO Unix 3.2.x regarding the the NULL pointer. The same
behavior exists for HP-UX, Sinix 5.4.1 and AIX.

Running the same program under Solaris 2.3 produces a segmentation
fault!

The problem is starkly exemplified in the following short program
`null.c' (of course, in the real program, the error doesn't manifest
as obviously):

#include <stdio.h>

void main ()
{

int *i = (int *) NULL;

printf("i == 0 Adr: %d Cont: '%s'\n",(int) i, (char *) i);
fflush(stdout);

/* Output SCO: i == 0 Adr: 0 Cont: '(null)' */
/* Output Sinix: i == 0 Adr: 0 Cont: '' */
/* Output SOLARIS2.3: Segmentation fault (core dumped) */
/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/

return;
}

Code like this occurs literally thousands of times in the program
(which tells a lot about the programming style, I know, but I didn't
write it).

I am able to use the GNU cc or the SUNSelect cc.
Now, I'm using gcc which means I'll be satisfied with a modified gcc or GNU
binutils. Or I'd like to have a fully automated method of changing the
source code, BUT I DON'T WANT TO DO IT MANUALLY!

Please reply by email, too. I'll post a summary if you want.

Thanks a lot in advance for your kind help,

yours

Michael
--

Friendly regards,
mit freundlichen Grüßen,

Michael Hühne

3-S GmbH

*----------------------- ro...@3-s.3-s.de -----------------------*
|................... svr...@Germany.EU.net ....................|
|... My first unix was ...... ////..... a Microsoft Xenix 86 ...|
|... Why does Microsoft ... 0(o o)0 ... develop a WINDOWS-NT ...|
*-------- Michael -------oOO--(_)--OOo---------- Hühne ---------*

#include <disclaimer.h>

Edward C. Hook

unread,
Nov 5, 1994, 9:10:35 PM11/5/94
to
In article <ROOT.94N...@technik.3-s.de>,

ro...@technik.3-s.de (Michael_Huehne) writes:
|> Hello there,
|>
|> I've got to port a large (200000+ lines of code) program to Solaris
|> 2.3. However, the program makes use of a strange feature of, for
|> example, SCO Unix 3.2.x regarding the the NULL pointer. The same
|> behavior exists for HP-UX, Sinix 5.4.1 and AIX.
|>
|> Running the same program under Solaris 2.3 produces a segmentation
|> fault!
|>
|> The problem is starkly exemplified in the following short program
|> `null.c' (of course, in the real program, the error doesn't manifest
|> as obviously):
|>
|> #include <stdio.h>
|>
|> void main ()
|> {
|>
|> int *i = (int *) NULL;
|>
|> printf("i == 0 Adr: %d Cont: '%s'\n",(int) i, (char *) i);
|> fflush(stdout);
|>
|> /* Output SCO: i == 0 Adr: 0 Cont: '(null)' */
|> /* Output Sinix: i == 0 Adr: 0 Cont: '' */
|> /* Output SOLARIS2.3: Segmentation fault (core dumped) */
|> /*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/
|>
|> return;
|> }
|>
|> Code like this occurs literally thousands of times in the program
|> (which tells a lot about the programming style, I know, but I didn't
|> write it).
|>
This is yet another example of the bad things that can happen if you stray
into the realm of undefined behavior ( ... and, no, I'm not talking just
about the infuriating "void main()" ... ). If you run a version of this
code under Solaris 2.3 and use 'dbx' to examine the resulting core file,
you'll discover that the segmentation fault is the result of doing
"strlen(NULL);" -- in fact, you can get an even better notion of the
problem by compiling and executing:

#include <stdio.h>

int sTrLeN(char *string);

int main(void)
{
printf("%d\n",sTrLeN(NULL));

return 0;
}

int sTrLeN(char *string)
{
int len = 0;

while ( *string ) {
++len;
++string;
}

return len;
}

It, too, drops core with a segmentation violation, and 'dbx' says:

program terminated by signal SEGV (segmentation violation)
(dbx) where
sTrLeN(string = (nil)), line 16 in "mike3.c"
main(), line 7 in "mike3.c"
(dbx) list
16 while ( *string ) {
17 ++len;
18 ++string;
19 }
20
21 return len;
22 }
23
(dbx) print len
len = 0
(dbx) quit

which shows that merely accessing what lives at the other end of a NULL
pointer is asking for trouble ... .

|> I am able to use the GNU cc or the SUNSelect cc.
|> Now, I'm using gcc which means I'll be satisfied with a modified gcc or GNU
|> binutils. Or I'd like to have a fully automated method of changing the
|> source code, BUT I DON'T WANT TO DO IT MANUALLY!
|>

Don't have much to offer by way of a workaround ... Sorry ...



|> Please reply by email, too. I'll post a summary if you want.
|>


--
Ed Hook | Coppula eam, se non posit
Computer Sciences Corporation / NAS | acceptera jocularum.
NASA/Ames Research Center | Me? Speak for my employer?...<*snort*>
Internet: ho...@nas.nasa.gov | ... Get a _clue_ !!! ...

Mats Olsson

unread,
Nov 6, 1994, 12:52:38 AM11/6/94
to
In article <Cytq1...@cnn.nas.nasa.gov>,

Edward C. Hook <ho...@nas.nasa.gov> wrote:
>In article <ROOT.94N...@technik.3-s.de>,
> ro...@technik.3-s.de (Michael_Huehne) writes:
>|> I've got to port a large (200000+ lines of code) program to Solaris
>|> 2.3. However, the program makes use of a strange feature of, for
>|> example, SCO Unix 3.2.x regarding the the NULL pointer. The same
>|> behavior exists for HP-UX, Sinix 5.4.1 and AIX.
> which shows that merely accessing what lives at the other end of a NULL
> pointer is asking for trouble ... .

The problem is that on the very first UNIX'es, adress zero would always
contain a zero, which made a string pointer that pointed to an empty
string and a null pointer equivalent. Ie,

char * s1 = NULL;
char * s2 = "";
printf("%d,%d", *s1,*s2);

would say give 0,0 as result. Convinient sometimes.

This isn't all that brilliant though, because using a null pointer
is almost always an error. So in the name of safe programming,
dereferencing NULL on Solaris and most modern UNIX'es results in a
segmentation violation.

As for a workaround... I'm not sure, but perhaps you could try
mmap(2) to make page zero readable... haven't tried it, but the manual
page doesn't seem to forbid it outright...

/Mats

Casper H.S. Dik

unread,
Nov 6, 1994, 7:00:13 AM11/6/94
to
ro...@technik.3-s.de (Michael_Huehne) writes:

>I've got to port a large (200000+ lines of code) program to Solaris
>2.3. However, the program makes use of a strange feature of, for
>example, SCO Unix 3.2.x regarding the the NULL pointer. The same
>behavior exists for HP-UX, Sinix 5.4.1 and AIX.

The program makes use of undefined behaviour. Anything can happen.

>Running the same program under Solaris 2.3 produces a segmentation
>fault!

Your program has bugs, Solaris 2.3 detects them, and you're unhappy?

> void main ()


Good thing you didn't crosspost to comp.lang.c, void main() is a no-no.

> int *i = (int *) NULL;

> printf("i == 0 Adr: %d Cont: '%s'\n",(int) i, (char *) i);
> fflush(stdout);

> /* Output SCO: i == 0 Adr: 0 Cont: '(null)' */
> /* Output Sinix: i == 0 Adr: 0 Cont: '' */
> /* Output SOLARIS2.3: Segmentation fault (core dumped) */
> /*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/

Yep, this is why OSes shouldn't allow sloppy code. People won't fix
it and they end up with *huge* bug-ridden programs that suddenly can't
be ported because newer OSes punish buggy behaviour.

>Code like this occurs literally thousands of times in the program
>(which tells a lot about the programming style, I know, but I didn't
>write it).
>
>I am able to use the GNU cc or the SUNSelect cc.
>Now, I'm using gcc which means I'll be satisfied with a modified gcc or GNU
>binutils. Or I'd like to have a fully automated method of changing the
>source code, BUT I DON'T WANT TO DO IT MANUALLY!


Try this:

#include <sys/mman.h>
#include <unistd.h>
#include <sys/fcntl.h>
main()

{

int fd = open ("/dev/zero", O_RDWR);
mmap(0, sysconf(_SC_PAGESIZE), PROT_READ, MAP_FIXED|MAP_PRIVATE, fd, 0);
close(fd);
printf("'%s'\n", 0);
}

Casper

Lawrence Kirby

unread,
Nov 6, 1994, 8:04:36 PM11/6/94
to
In article <ROOT.94N...@technik.3-s.de>
svr...@Germany.EU.net "Michael_Huehne" writes:

> void main ()

Bad start - see any of the many threads with 'void main' in the subject
or FAQ section 5.12.

> {
>
> int *i = (int *) NULL;
>
> printf("i == 0 Adr: %d Cont: '%s'\n",(int) i, (char *) i);
> fflush(stdout);
>
> /* Output SCO: i == 0 Adr: 0 Cont: '(null)' */
> /* Output Sinix: i == 0 Adr: 0 Cont: '' */
> /* Output SOLARIS2.3: Segmentation fault (core dumped) */
> /*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/

Is your problem specifically how printf works or whether in general
dereferencing NULL causes a segmentation fault?

--
-----------------------------------------
Lawrence Kirby | fr...@genesis.demon.co.uk
Wilts, England | 7073...@compuserve.com
-----------------------------------------

Tilman Schmidt

unread,
Nov 7, 1994, 11:02:51 AM11/7/94
to
In article <ROOT.94N...@technik.3-s.de>,

Michael_Huehne <svr...@Germany.EU.net> wrote:
>I've got to port a large (200000+ lines of code) program to Solaris
>2.3. However, the program makes use of a strange feature of, for
>example, SCO Unix 3.2.x regarding the the NULL pointer. The same
>behavior exists for HP-UX, Sinix 5.4.1 and AIX.
>
>Running the same program under Solaris 2.3 produces a segmentation
>fault!
[...]

> int *i = (int *) NULL;
>
> printf("i == 0 Adr: %d Cont: '%s'\n",(int) i, (char *) i);
> fflush(stdout);
>
> /* Output SCO: i == 0 Adr: 0 Cont: '(null)' */
> /* Output Sinix: i == 0 Adr: 0 Cont: '' */
> /* Output SOLARIS2.3: Segmentation fault (core dumped) */

The behaviour under SCO, HP-UX etc. is not a feature of the NULL
pointer itself, but a feature of printf() that I have been very
fond of, and that for reasons unknown to me, Sun chose to drop in
Solaris 2: If the argument passed for a %s format is NULL, it
would not attempt to actually read from address zero, but
substitute the fixed string "(null)" instead. This was
particularly useful in the case of diagnostic outputs because a
NULL pointer could just be the reason why you needed them.
Obviously, this can't be mended by changing the compiler, only by
using a different libc.a (where printf() lives).

The Sinix case, OTOH, is just an instance of a system where reading
from address zero actually works and returns a zero value. The
same is true for example on Ultrix on a DECstation. Folklore has
it that this was the normal behaviour on prehistoric Unices and led
many to the bad habit of carelessly dereferencing NULL pointers, or
even relying on the assumption that *0 == 0 (shudder!)

--
Tilman Schmidt Phone: +49 221 8299 275
Sema Group Deutschland GmbH Fax: +49 221 8299 266
Siegburger Str. 215, 50679 Koeln, Germany E-Mail: t...@gb1.sema.de

Craig R. McClanahan

unread,
Nov 7, 1994, 9:39:58 PM11/7/94
to
>>>>> "Mats" == Mats Olsson <ma...@DTEK.CHALMERS.SE> writes:

Mats> In article <Cytq1...@cnn.nas.nasa.gov>, Edward C. Hook
Mats> <ho...@nas.nasa.gov> wrote:

>> In article <ROOT.94N...@technik.3-s.de>,
>> ro...@technik.3-s.de (Michael_Huehne) writes:

>>> I've got to port a large (200000+ lines of code) program to Solaris
>>> 2.3. However, the program makes use of a strange feature of, for
>>> example, SCO Unix 3.2.x regarding the the NULL pointer.
>>> The same behavior exists for HP-UX, Sinix 5.4.1 and AIX.

>>> which shows that merely accessing what lives at the other end
>>> of a NULL pointer is asking for trouble ... .

Mats> The problem is that on the very first UNIX'es, adress
Mats> zero would always contain a zero, which made a string
Mats> pointer that pointed to an empty string and a null pointer
Mats> equivalent. Ie,

Mats> char * s1 = NULL; char * s2 = ""; printf("%d,%d", *s1,*s2);

Mats> would say give 0,0 as result. Convinient sometimes.

It is also very bad programming practice -- yes, I know that doesn't help
much when you're stuck converting old code :-(

Mats> This isn't all that brilliant though, because using a
Mats> null pointer is almost always an error. So in the name of
^^ ^^^^^^ ^^^^^^ ^^ ^^^^^
Mats> safe programming, dereferencing NULL on Solaris and most
Mats> modern UNIX'es results in a segmentation violation.

It's not just an issue of being an error -- in ANSI Standard C, it is also an
illegal operation (i.e. pilot error), and is thus practically guaranteed to
not be portable. In light of this, Solaris crashing on a SEGV violation when
you *do* dereference a NULL pointer is a lot better (because it points out a
user bug) than blindly proceeding on with an unknown value.

Mats> As for a workaround... I'm not sure, but perhaps you
Mats> could try mmap(2) to make page zero readable... haven't
Mats> tried it, but the manual page doesn't seem to forbid it
Mats> outright...

Mats> /Mats

Even if this works, it's going to be *really* platform (and perhaps even
compiler revision) dependent. It sounds like an absolute last resort kind
of solution.

----------
Craig R. McClanahan EMAIL: c...@dat.com
DAT Services Phone: 503-643-4331
Beaverton, OR, USA Fax: 503-526-6442

Casper H.S. Dik

unread,
Nov 8, 1994, 4:26:28 AM11/8/94
to
t...@gb1.sema.de (Tilman Schmidt) writes:

>The behaviour under SCO, HP-UX etc. is not a feature of the NULL
>pointer itself, but a feature of printf() that I have been very
>fond of, and that for reasons unknown to me, Sun chose to drop in
>Solaris 2: If the argument passed for a %s format is NULL, it
>would not attempt to actually read from address zero, but
>substitute the fixed string "(null)" instead. This was
>particularly useful in the case of diagnostic outputs because a
>NULL pointer could just be the reason why you needed them.
>Obviously, this can't be mended by changing the compiler, only by
>using a different libc.a (where printf() lives).

I think the computer should only do so much handholding.

And this is a perfect examples of why: while the "(null)" output
of printf is useful, *when debugging*, such uses of NULL could go
undetected for years, until you port to an OS that isn't so nice.

Handholding and sloppy error checking make for unportable code.

Casper

Message has been deleted

Peter Seebach

unread,
Nov 9, 1994, 8:00:55 AM11/9/94
to
In article <1994Nov8.1...@kf8nh.wariat.org> b...@kf8nh.wariat.org (Brandon S. Allbery) writes:
>In article <39ng84$p...@mail.fwi.uva.nl>, cas...@fwi.uva.nl (Casper H.S. Dik) says:
>Especially nasty because, while "printf("%s\n", (char *) 0);" prints "(null)"
>under SCO, "strcpy(s, (char *) 0); printf(:%s\n", s);" prints "L\1\4". Try
>debugging *that*! I much prefer leaving page 0 unmapped.

Well, you have to allocate space for s first, otherwise you're invoking
undefined behavior.

>Brandon S. Allbery KF8NH [44.70.4.88] b...@kf8nh.wariat.org
>Linux development: iBCS2, JNOS, MH ~\U
>Controlling application developers is like herding cats. --Oracle DBA Manual

-seebs
--
Peter Seebach - se...@solutions.solon.com -- se...@intran.xerox.com
C/Unix proto-wizard -- C/Unix questions? Send mail for help.

Troy De Jongh

unread,
Nov 8, 1994, 3:53:43 PM11/8/94
to
> From: b...@kf8nh.wariat.org (Brandon S. Allbery)

>
> In article <39ng84$p...@mail.fwi.uva.nl>, cas...@fwi.uva.nl (Casper H.S. Dik) says:
> +---------------

> | And this is a perfect examples of why: while the "(null)" output
> | of printf is useful, *when debugging*, such uses of NULL could go
> | undetected for years, until you port to an OS that isn't so nice.
> +------------->8

>
> Especially nasty because, while "printf("%s\n", (char *) 0);" prints "(null)"
> under SCO, "strcpy(s, (char *) 0); printf(:%s\n", s);" prints "L\1\4". Try
> debugging *that*! I much prefer leaving page 0 unmapped.
>
> ++Brandon
> --
> Brandon S. Allbery KF8NH [44.70.4.88] b...@kf8nh.wariat.org
> Linux development: iBCS2, JNOS, MH ~\U
> Controlling application developers is like herding cats. --Oracle DBA Manual
>

The reason virtual address 0 can be accessed on SCO is that the
executable is generated as having the text start at virtual address 0.
For example, main() usually has an address of around 0x128. And
since XSEG's are read-only, a process can read contents at address 0. A
developer can protect himself by using the '-z' option during
linking, or '-link -z' during compilation (cc passes the -z down
to the linker). This will force the text to be placed at virtual
address 0x20000, for example, and then main will be 0x20128 (according
to the example above). That way, you get what you wanted: page 0
being unmapped for this executable's address space during execution,
and a SIGSEGV signal when "accidentally" reading from address 0. ;-)


--
Troy De Jongh Digiboard (tr...@digibd.com)

Bruce Barnett

unread,
Nov 9, 1994, 10:01:47 AM11/9/94
to

Since you should be using lint or the equivalent,
and since you should be checking the error returns of the printf
you probably have printf wrapped in another function, right?

Given all that, why not make your special version of printf check for
null pointers? Why depend on the vendor to do this?

--
Bruce Barnett <bar...@crd.ge.com> uunet!crdras!barnett

Rod Evans

unread,
Nov 10, 1994, 1:25:16 PM11/10/94
to

Of course, if you are dynamically linked, you can always arrange
to accommodate a null pointer. From an inventive colleague:

% cat 0@0.c
/*
* Copyright (c) 1994 by Sun Microsystems, Inc.
*
* Map in a read-only page of zeroes at location zero, for stupid
* programs that think a null pointer is as good as a null string.
*
* Use:
* LD_PRELOAD=0...@0.so.1 program args ...
*
*/
#pragma ident "@(#)0@0.c 1.1 94/05/10 SMI"

#include <sys/types.h>
#include <sys/mman.h>
#include <sys/fcntl.h>

#pragma init(__zero_at_zero)

static void
__zero_at_zero()
{
int fd;

if ((fd = open("/dev/zero", O_RDWR)) < 0)
return;
(void) mmap(0, 1, PROT_READ, MAP_PRIVATE|MAP_FIXED, fd, 0);
(void) close(fd);
}
% cc -G -o 0...@0.so.1 -Kpic 0@0.c
% cat main.c
main()
{
int * foo = 0;
printf("`%d'\n", *foo);
}
% main
Segmentation Fault(coredump)
% LD_PRELOAD=./0...@0.so.1 main
`0'

Rod.

Neal P. Murphy

unread,
Nov 8, 1994, 5:10:27 PM11/8/94
to
t...@gb1.sema.de (Tilman Schmidt) writes:

>In article <ROOT.94N...@technik.3-s.de>,
>Michael_Huehne <svr...@Germany.EU.net> wrote:
>>I've got to port a large (200000+ lines of code) program to Solaris
>>2.3. However, the program makes use of a strange feature of, for
>>example, SCO Unix 3.2.x regarding the the NULL pointer. The same
>>behavior exists for HP-UX, Sinix 5.4.1 and AIX.
>>
>>Running the same program under Solaris 2.3 produces a segmentation
>>fault!
>[...]
>> int *i = (int *) NULL;
>>
>> printf("i == 0 Adr: %d Cont: '%s'\n",(int) i, (char *) i);
>> fflush(stdout);
>>
>> /* Output SCO: i == 0 Adr: 0 Cont: '(null)' */
>> /* Output Sinix: i == 0 Adr: 0 Cont: '' */
>> /* Output SOLARIS2.3: Segmentation fault (core dumped) */

>The Sinix case, OTOH, is just an instance of a system where reading


>from address zero actually works and returns a zero value. The
>same is true for example on Ultrix on a DECstation. Folklore has
>it that this was the normal behaviour on prehistoric Unices and led
>many to the bad habit of carelessly dereferencing NULL pointers, or
>even relying on the assumption that *0 == 0 (shudder!)

SysV/88 has a sysgen parameter that allows one to determine if the system
traps null dereferences: TRAPNULLPT. I think this is along the lines of
what you want. Since Solaris 2 is a SysV variant, it *might* have a similar
configurable parameter.

Fester

Message has been deleted

Tilman Schmidt

unread,
Nov 14, 1994, 6:40:18 AM11/14/94
to
In article <BARNETT.94...@grymoire.crd.ge.com>,

Bruce Barnett <bar...@grymoire.crd.ge.com> wrote:
>
>Since you should be using lint or the equivalent,
>and since you should be checking the error returns of the printf
>you probably have printf wrapped in another function, right?

Wrong. Why should I dig up old lint from its grave when gcc
warns me about anything I want to be warned about - *without*
bothering me about not using fprintf's return value?

>Given all that, why not make your special version of printf check for
>null pointers? Why depend on the vendor to do this?

Because it appears pretty wasteful to me to write my own parser
for printf format strings just to find out which arguments are
pointers so I can check them for NULLs, when printf parses the
format string anyway and could do the same with a single 'if'.
In that case, I much prefer writing

fprintf(stderr, "Stuff was %s\n", stuff ? stuff : "(null)");

each and every time, which is what I do now, but it does clutter
the code considerably.

Bruce Barnett

unread,
Nov 15, 1994, 12:04:51 PM11/15/94
to
In article <Cz99r...@gb1.sema.de> t...@gb1.sema.de (Tilman Schmidt) writes:

> In article <BARNETT.94...@grymoire.crd.ge.com>,
> I wrote:

> >Since you should be using lint or the equivalent,
> >and since you should be checking the error returns of the printf
> >you probably have printf wrapped in another function, right?

> Wrong. Why should I dig up old lint from its grave when gcc
> warns me about anything I want to be warned about - *without*
> bothering me about not using fprintf's return value?

I said: "lint or equivalent". Obviously ANSI C can do parameter type
checking.

You don't check fprintf error returns? What happens when your disk fills up?

John Caruso

unread,
Nov 15, 1994, 5:20:04 PM11/15/94
to
In article <Cz99r...@gb1.sema.de>, Tilman Schmidt <t...@gb1.sema.de> wrote:
>
>In that case, I much prefer writing
>
> fprintf(stderr, "Stuff was %s\n", stuff ? stuff : "(null)");
>
>each and every time, which is what I do now, but it does clutter
>the code considerably.

To avoid the clutter, just: #define NOTNULL(p) ((p) ? (p) : "(null)")
and use NOTNULL(pointer) in your printf calls.

---------------------------------------------------------------------
John Caruso car...@nova.umuc.edu
Unix/VMS System Administrator caruso@UMUC (Bitnet)
University of Maryland University College (301) 985-7447
---------------------------------------------------------------------

Mandeep S Dhami

unread,
Nov 27, 1994, 1:58:05 AM11/27/94
to
In <3abc6k$5...@nova.umd.edu> car...@nova.umd.edu (John Caruso) writes:
>To avoid the clutter, just: #define NOTNULL(p) ((p) ? (p) : "(null)")
>and use NOTNULL(pointer) in your printf calls.

If you use GNU cc, you may prefer the following (a GNU extension) which
ensures that you don't kill yourself in case p had side effects:

#define NOTNULL(p) ((p)?:"(null)")

for example, with first method,
while (*argv) printf("Next arg - %s\n", NOTNULL(*argv++));
misses even args, and dumps core # arguments = even. Unfortunately, it
is not ANSI (as far as I know).

Mandeep
------------------------------------------------------------------------------

0 new messages