Форум программистов, компьютерный форум, киберфорум
C/С++ под Linux
Войти
Регистрация
Восстановить пароль
Карта форума Темы раздела Блоги Сообщество Поиск Заказать работу  
 
 
Рейтинг 4.85/34: Рейтинг темы: голосов - 34, средняя оценка - 4.85
440 / 97 / 42
Регистрация: 04.10.2011
Сообщений: 346
1

Getwchar stack smashing detected

11.12.2018, 21:56. Показов 6439. Ответов 25

Author24 — интернет-сервис помощи студентам
Всем привет. Кто сможет объяснить что происходит, вот этот код:

C
1
2
3
4
5
6
7
8
9
10
11
12
13
#include <locale.h>
#include <wchar.h>
 
int main()
{
    setlocale(LC_ALL, "ru_RU.UTF-8");
 
    for (int i = 0; i < 10; i++) {
        if (WEOF == (getwchar()) ) break;
    }
 
    return 0;
}
Выдает ошибку переполнения буфера, если ввести не два символа, а строку примерно символов в восемь-девять:


$ ./myproga
qwrqwerwqerwqerewqrqwerqwerqwerqwerwqerqwerqwerqwrqrwqerwqerqwr
*** stack smashing detected ***: <unknown> terminated
Press <RETURN> to close this window...


Кто подскажет где я умудрился ему стек переполнить?
0
Programming
Эксперт
94731 / 64177 / 26122
Регистрация: 12.04.2006
Сообщений: 116,782
11.12.2018, 21:56
Ответы с готовыми решениями:

stack smashing detected
Пишу приложение в qt. После того, как оно отрабатывает, на самой последней строке, закрывающей...

Stack smashing detected + Массивы + Random
Здравствуйте. Пытаюсь реализовать программу, которая выполняла бы несколько задач. 1) Вычисление...

Stack smashing detected. Ребята, перемешка данных, что не так?
#include &lt;iostream&gt; using namespace std; int main() { setlocale(LC_ALL, &quot;Russian&quot;); int...

*** stack smashing detected ***: <unknown> terminated, программа завершает работу на цикле
Здравствуйте! Пробую сделать программу, которая заполняет массив случайными числами от 10 до 100 и...

25
923 / 639 / 198
Регистрация: 08.09.2013
Сообщений: 1,693
12.12.2018, 21:52 2
Цитата Сообщение от moskitos80 Посмотреть сообщение
Кто подскажет где я умудрился ему стек переполнить?
Похоже, это баг libc, так как clang демонстрирует подобное поведение.
То, что getwchar() редко используется, мало их оправдывает.
Увеличение стека с 8 до 100 К позволяет в несколько раз удлинить входной поток. Но все равно выскакивает либо корка, либо переполнение стека.

Добавлено через 2 минуты
libc6:amd64 2.27-8
gcc (Debian 8.2.0-9) 8.2.0

Добавлено через 16 минут
А вот у более старой
libc6:amd64 2.13-38
все в порядке
1
590 / 413 / 135
Регистрация: 02.10.2008
Сообщений: 1,745
Записей в блоге: 1
12.12.2018, 22:08 3
Цитата Сообщение от gng Посмотреть сообщение
Похоже, это баг libc, так как clang демонстрирует подобное поведение.
То, что getwchar() редко используется, мало их оправдывает.
Нет. Это не бага, а "фишка" потоков в Си. В винде и макоси отошли от стандарта и решили исправить эту "фишку"(в винде вообще сильно расширили "многобайтное")

Они бывают "неориентированные", "узкой ориентации" и "широкой ориентации"
Так вот, пока поток только открыт, но ещё не использовался - он "неориентированный" и первый вызов к-либо функции ввода/вывода "ориентирует" его в соотв. с этой функцией.
Например(учтите, что getwchar() обращается к fgetwc()):
C
1
2
3
4
int main()
{
 fgetc(stdin); /*сориентирует поток в "узкий", т.е. он будет однобайтным ASCII*/
return 0;}
C
1
2
3
4
int main()
{
 fgetwc(stdin); /*сориентирует поток в "широкий", т.е. он будет многобайтным Unicode*/
return 0;}
По стандарту в дальнейшем сменить ориентацию уже нельзя, единственный "стандартный" выход - freopen().
setlocale, по всей видимости взаимодействует с открытыми stdin/stdout переводя их "направленность" в значение по дефолту (узкое), потому и косяк вылазит при wide-char работе.

Для кода ТС стоит убрать setlocale() - ошибка исчезнет, стоит обратиться wide-функцией к stdin до вызова setlocale() - ошибка тоже исчезнет:
Кликните здесь для просмотра всего текста

Bash
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
faust@archlinux РАзная всячина]$ cat 1.c && gcc 1.c && ./a.out
#include <locale.h>
#include <stdio.h>
#include <wchar.h>
 
int main()
{
    /*Делаем поток stdin "широкой направленности", надеюсь не суксуальной.*/
    getwchar();
    
    setlocale(LC_ALL, "ru_RU.UTF-8"); /*а вот тут уже до лампочки 
        обращения setlocale к потоку stdin - он уже "широкий" и сегфолтов/стеков уже не будет*/
 
    for (int i = 0; i < 10; i++) {
        if (WEOF == (getwchar()) ) break;
    }
 
    return 0;
}
11111111111111111111111111111111111111111111111111111111111222222222222222222222222222222222222222222222222222222222222222222233333333333333333333333333333333333333333333333333333333444444444444444444444444444444444444444444444444444455555555555555555555555555555555555555555555555555555555555555555555555666666666666666666666666666666666666666666666666666666666777777777777777777777777777777777777777777777777788888888888888888888888888888888888888888888888888888888888999999999999999999999999999999999999999999999999999999000000000000000000000000000000000000000000000000000000000000000
[faust@archlinux РАзная всячина]$
3
923 / 639 / 198
Регистрация: 08.09.2013
Сообщений: 1,693
12.12.2018, 22:25 4
Цитата Сообщение от drfaust Посмотреть сообщение
Это не бага, а "фишка" потоков в Си.
Интересная фишка. Старые libc об этом еще не знали?
По мне, это явный баг. Пусть тогда проверяют ориентацию потока и возвращают ошибку.
0
590 / 413 / 135
Регистрация: 02.10.2008
Сообщений: 1,745
Записей в блоге: 1
12.12.2018, 22:34 5
Цитата Сообщение от gng Посмотреть сообщение
Старые libc об этом еще не знали?
Они её "обходили", по примеру макосей и виндов, при этом отходя от стандарта. А может это сами стандарты поменялись(решили таким неудобным макаром упорядочить "узких" и "широких")

Надо попробовать без freopen() через stat() и setmode(). Вроде, не меняя то, что вернёт stat() и зарядив это в setmode можно "сбросить" ориентированность. Так же что-то читал про сброс буферов, персонально по stdin/stdout
0
724 / 224 / 72
Регистрация: 01.03.2011
Сообщений: 629
13.12.2018, 20:12 6
Цитата Сообщение от drfaust Посмотреть сообщение
setlocale, по всей видимости взаимодействует с открытыми stdin/stdout переводя их "направленность" в значение по дефолту (узкое), потому и косяк вылазит при wide-char работе.
fwide(stdin, 0) возвращает 0 - т.е. "без ориентации" как до, так и после setlocale() в этом примере. Правда если сделать fwide(stdin, 1) до setlocale() все работает хорошо. Получается, что если setlocale() и ставит какие-то флаги для stdin , то делает это как-то молча. ИМХО баг.
1
590 / 413 / 135
Регистрация: 02.10.2008
Сообщений: 1,745
Записей в блоге: 1
13.12.2018, 23:49 7
Цитата Сообщение от prik Посмотреть сообщение
Получается, что если setlocale() и ставит какие-то флаги для stdin , то делает это как-то молча. ИМХО баг.
Нифига не понимаю где эта самая "направленность" хранится.
Прогнал код через strace - c setlocale и без. Да, setlocale дважды открывает свои файлы /usr/lib/locale/locale-archive и /usr/lib/x86_64-linux-gnu/gconv/gconv-modules.cache и только потом (перед первым использованием stdin) вызывается stat() и read() для stdin`а открытого сразу execve(наша_косячная_прога)

Выхлоп стартап-кода вырезал - там всё одинаково.
Косячный
Кликните здесь для просмотра всего текста

brk(NULL) = 0x55e2d88b4000
brk(0x55e2d88d5000) = 0x55e2d88d5000
open("/usr/lib/locale/locale-archive", O_RDONLY|O_CLOEXEC) = 3
fstat(3, {st_dev=makedev(8, 1), st_ino=5247872, st_mode=S_IFREG|0644, st_nlink=1, st_uid=0, st_gid=0, st_blksize=4096, st_blocks=3144, st_size=1679808, st_atime=2018-12-13T21:26:50+0500.124037805, st_mtime=2018-03-20T13:00:34+0500.821409394, st_ctime=2018-03-20T13:00:34+0500.821409394}) = 0
mmap(NULL, 1679808, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7f5456828000
close(3) = 0
open("/usr/lib/x86_64-linux-gnu/gconv/gconv-modules.cache", O_RDONLY) = 3
fstat(3, {st_dev=makedev(8, 1), st_ino=5251089, st_mode=S_IFREG|0644, st_nlink=1, st_uid=0, st_gid=0, st_blksize=4096, st_blocks=56, st_size=26258, st_atime=2018-12-13T21:26:50+0500.556039947, st_mtime=2018-01-14T15:39:44+0500, st_ctime=2018-03-20T12:57:18+0500.885313523}) = 0
mmap(NULL, 26258, PROT_READ, MAP_SHARED, 3, 0) = 0x7f54569d5000
close(3) = 0
fstat(0, {st_dev=makedev(0, 17), st_ino=4, st_mode=S_IFCHR|0620, st_nlink=1, st_uid=1000, st_gid=5, st_blksize=1024, st_blocks=0, st_rdev=makedev(136, 1), st_atime=2018-12-14T01:25:12+0500.476080882, st_mtime=2018-12-14T01:25:12+0500.476080882, st_ctime=2018-12-14T01:10:19+0500.476080882}) = 0
read(0, "123123\n", 1024) = 7
read(0, "\n", 1024) = 1
read(0, "1231\n", 1024) = 5
lseek(0, -5, SEEK_CUR) = -1 ESPIPE (Illegal seek)
exit_group(0) = ?
+++ exited with 0 +++

Без setlocale
Кликните здесь для просмотра всего текста

fstat(0, {st_dev=makedev(0, 17), st_ino=4, st_mode=S_IFCHR|0620, st_nlink=1, st_uid=1000, st_gid=5, st_blksize=1024, st_blocks=0, st_rdev=makedev(136, 1), st_atime=2018-12-14T01:18:16+0500.476080882, st_mtime=2018-12-14T01:18:16+0500.476080882, st_ctime=2018-12-14T01:10:19+0500.476080882}) = 0
brk(NULL) = 0x56344f007000
brk(0x56344f028000) = 0x56344f028000
read(0, "12311111111111111111111111111\n", 1024) = 30
lseek(0, -20, SEEK_CUR) = -1 ESPIPE (Illegal seek)
exit_group(0) = ?
+++ exited with 0 +++


Учитывая, что работа идёт через read(), которому до лампочки всё(просто поток байт), то рыть надо сам libc

Просто жутко интересно, вроде должно пахать - ан нет... Обходных манёвров уже насобирали, а вот что бы понять суть...
0
440 / 97 / 42
Регистрация: 04.10.2011
Сообщений: 346
14.12.2018, 00:00  [ТС] 8
Да вот суть хотелось бы понять. У самого у меня квалификации не хватит разобраться я к сожалению только в начале пути. Но с удовольствием понаблюдаю за дискуссией.
0
590 / 413 / 135
Регистрация: 02.10.2008
Сообщений: 1,745
Записей в блоге: 1
14.12.2018, 00:03 9
Покопался в fwide() - мультибайт расширяет привычную нам структуру FILE, там появляется и свой буфер, и привязывается iconv и ещё что-то... И, самое главное, то, чем манипулирует fwide() - поле _mode (fwide его нормализирует в -1,0,1 , обеспечивает блокировки, проверки и т.п.)
0
724 / 224 / 72
Регистрация: 01.03.2011
Сообщений: 629
14.12.2018, 09:38 10
Покрайней мере у меня оно падает именно в libc при выходе.
#0 __GI__IO_wfile_sync (fp=0x0) at wfileops.c:534
Вот эта ф-я:
C
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
_IO_wfile_sync (FILE *fp)
{
  ssize_t delta;
  wint_t retval = 0;
  /*    char* ptr = cur_ptr(); */
  if (fp->_wide_data->_IO_write_ptr > fp->_wide_data->_IO_write_base)
    if (_IO_do_flush (fp))
      return WEOF;
  delta = fp->_wide_data->_IO_read_ptr - fp->_wide_data->_IO_read_end;
  if (delta != 0)
    {
      /* We have to find out how many bytes we have to go back in the
         external buffer.  */
      struct _IO_codecvt *cv = fp->_codecvt;
      off64_t new_pos;
      int clen = (*cv->__codecvt_do_encoding) (cv);
      if (clen > 0)
        /* It is easy, a fixed number of input bytes are used for each
           wide character.  */
        delta *= clen;
      else
        {
          /* We have to find out the hard way how much to back off.
             To do this we determine how much input we needed to
             generate the wide characters up to the current reading
             position.  */
          int nread;
          fp->_wide_data->_IO_state = fp->_wide_data->_IO_last_state;
          nread = (*cv->__codecvt_do_length) (cv, &fp->_wide_data->_IO_state,
                                              fp->_IO_read_base,
                                              fp->_IO_read_end, delta);
          fp->_IO_read_ptr = fp->_IO_read_base + nread;
          delta = -(fp->_IO_read_end - fp->_IO_read_base - nread);
        }
      new_pos = _IO_SYSSEEK (fp, delta, 1);
      if (new_pos != (off64_t) EOF)
        {
          fp->_wide_data->_IO_read_end = fp->_wide_data->_IO_read_ptr;
          fp->_IO_read_end = fp->_IO_read_ptr;
        }
      else if (errno == ESPIPE)
        ; /* Ignore error from unseekable devices. */
      else
        retval = WEOF;
    }
  if (retval != WEOF)
    fp->_offset = _IO_pos_BAD;
  /* FIXME: Cleanup - can this be shared? */
  /*    setg(base(), ptr, ptr); */
  return retval;
}
libc_hidden_def (_IO_wfile_sync)
Падает из-за отрицательной delta. Откуда она берется, пока не разбирался. Будет время/настроение поковыряю вечером.
А вообще надо glibcшникам баг открыть, пусть сами ковыряются.
1
590 / 413 / 135
Регистрация: 02.10.2008
Сообщений: 1,745
Записей в блоге: 1
14.12.2018, 09:54 11
Думаю дело не в этой функции. Неверно инициализируются FILE структуры... Надо будет сравнить содержимое stdin до вызова setlocale и и после. Так же глянуть в какой именно момент при fgetwc устанавливается "направленность" потока.

Что-то и где-то они забыли проинициализировать...

Если же поменять if (delta != 0) на if (delta > 0), то вместо вылета будем получать WEOF или к-нить errno, что тоже не красиво.
0
724 / 224 / 72
Регистрация: 01.03.2011
Сообщений: 629
14.12.2018, 10:28 12
Цитата Сообщение от drfaust Посмотреть сообщение
Что-то и где-то они забыли проинициализировать..
Я и не пытался понять где первоисточник граблей. Просто смотрю отладчиком где оно падает. В моем листинге 29 стока. Вызывается ф-я do_length() из iofwide.c вот она:
C
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
do_length (struct _IO_codecvt *codecvt, __mbstate_t *statep,
           const char *from_start, const char *from_end, size_t max)
{
  int result;
  const unsigned char *cp = (const unsigned char *) from_start;
  wchar_t to_buf[max];
  struct __gconv_step *gs = codecvt->__cd_in.__cd.__steps;
  size_t dummy;
  codecvt->__cd_in.__cd.__data[0].__outbuf = (unsigned char *) to_buf;
  codecvt->__cd_in.__cd.__data[0].__outbufend = (unsigned char *) &to_buf[max];
  codecvt->__cd_in.__cd.__data[0].__statep = statep;
  __gconv_fct fct = gs->__fct;
#ifdef PTR_DEMANGLE
  if (gs->__shlib_handle != NULL)
    PTR_DEMANGLE (fct);
#endif
  DL_CALL_FCT (fct,
               (gs, codecvt->__cd_in.__cd.__data, &cp,
                (const unsigned char *) from_end, NULL,
                &dummy, 0, 0));
  result = cp - (const unsigned char *) from_start;
  return result;
}
отрицательная delta == max в ней. И wchar_t to_buf[max]; ломает нам стэк.

Добавлено через 20 минут
Цитата Сообщение от drfaust Посмотреть сообщение
Если же поменять if (delta != 0) на if (delta > 0), то вместо вылета будем получать WEOF или к-нить errno, что тоже не красиво.
Не. На вскидку (зуба не дам) фиксить надо 9ю строку.
Думаю там должно быть такое.
C
1
delta = fp->_wide_data->_IO_read_end - fp->_wide_data->_IO_read_ptr;
Да и вообще я бы написал _IO_size_t delta; вместо IO_ssize_t delta;

Добавлено через 1 минуту
Но, нужно глубже вникать в это, а что-то не хочется
0
590 / 413 / 135
Регистрация: 02.10.2008
Сообщений: 1,745
Записей в блоге: 1
14.12.2018, 20:07 13
Бу-га-га...
C
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include <string.h>
#include <errno.h>
#include <locale.h>
#include <stdio.h>
#include <wchar.h>
 
int main()
{
    
    setlocale(LC_ALL, "ru_RU.UTF-8"); /*а вот тут уже до лампочки 
        обращения setlocale к потоку stdin - он уже "широкий" и сегфолтов/стеков уже не будет*/
    printf("1. errno = %s\n", strerror(errno));
 
    for (int i = 0; i < 10; i++) {
        if (WEOF == (getwc(stdin))) break;
    }
 
    printf("2. errno = %s\n", strerror(errno));
    return 0;
}
1. errno = Выполнено
11111111111111111111111111
2. errno = Выполнено
*** stack smashing detected ***: <unknown> terminated
Аварийный останов (стек памяти сброшен на диск)
И да, выбросил цикл, просто две команды - setlocale и getwc падают в корку
0
724 / 224 / 72
Регистрация: 01.03.2011
Сообщений: 629
14.12.2018, 21:16 14
setlocale() не виноватая вообще. Она и близко не прикасается к stdin. Баг будет проявляться на всех кодировках с символами переменной длины т.е. когда ф-ция do_encoding() возвращает -1;
Запустил пересборку glibc с патчем из 12го сообщения. Дома ноут старенький, минут 7 еще ждать

Добавлено через 24 минуты
Дождался. Вот это помогает, и вроде ничего не ломает.
Код
--- glibc-2.28.old/libio/wfileops.c	2018-08-01 08:10:47.000000000 +0300
+++ glibc-2.28/libio/wfileops.c	2018-12-14 21:03:17.759346239 +0300
@@ -480,14 +480,14 @@
 wint_t
 _IO_wfile_sync (FILE *fp)
 {
-  ssize_t delta;
+  size_t delta;
   wint_t retval = 0;
 
   /*    char* ptr = cur_ptr(); */
   if (fp->_wide_data->_IO_write_ptr > fp->_wide_data->_IO_write_base)
     if (_IO_do_flush (fp))
       return WEOF;
-  delta = fp->_wide_data->_IO_read_ptr - fp->_wide_data->_IO_read_end;
+  delta = fp->_wide_data->_IO_read_end - fp->_wide_data->_IO_read_ptr;
   if (delta != 0)
     {
       /* We have to find out how many bytes we have to go back in the
Добавлено через 7 минут
Да, и в качестве воркэраунда имхо лучшим вариантом будет __fpurge(stdin); перед выходом.
2
590 / 413 / 135
Регистрация: 02.10.2008
Сообщений: 1,745
Записей в блоге: 1
15.12.2018, 09:47 15
Цитата Сообщение от prik Посмотреть сообщение
--delta = fp->_wide_data->_IO_read_ptr - fp->_wide_data->_IO_read_end;
Чёт не въехал. Это они из текущего указателя вычитают конец??? Тогда как оно работает в остальных случаях???
0
724 / 224 / 72
Регистрация: 01.03.2011
Сообщений: 629
15.12.2018, 10:11 16
Цитата Сообщение от drfaust Посмотреть сообщение
Чёт не въехал. Это они из текущего указателя вычитают конец???
Да, все именно так. Очень похоже на банальную описку.
Цитата Сообщение от drfaust Посмотреть сообщение
Тогда как оно работает в остальных случаях???
Так для всех остальных случаев clen (это признак кодировки с переменным размером символов, который возвращает do_encoding() и именно его устанавливает fwide() глядя на текущую локаль)не отрицательный, и этот код просто не отрабатывает.
Что меня больше всего удивляет, коду более 15 лет и я не смог нагуглить ни одного упоминания о проблеме.
0
923 / 639 / 198
Регистрация: 08.09.2013
Сообщений: 1,693
15.12.2018, 15:42 17
Цитата Сообщение от prik Посмотреть сообщение
Что меня больше всего удивляет, коду более 15 лет и я не смог нагуглить ни одного упоминания о проблеме.
То, что при работе с utf-8 локалью широкие символы используют редко - не новость, но тем не менее то, что "детский" баг в широко используемой функции обнаружил уважаемый moskitos80 через 15 лет - действительно поражает!
Поленится или нет moskitos80 открыть баг glibc, но патч prik будет в любом случае востребован.
0
724 / 224 / 72
Регистрация: 01.03.2011
Сообщений: 629
15.12.2018, 18:13 18
Цитата Сообщение от gng Посмотреть сообщение
баг в широко используемой функции
Проблема все-таки не в ней. А в коде выполняемом после return 0; где среди прочих деструкторов делают аналог fflush() для всех открытых FILE*. Если для обычных файлов и stdout/stderr это понятно логично и правильно, то зачем они делают это для stdin я не понимаю. Ну прочитали мы сколько-то "лишних" байт из stdin и что с ними можно сделать при выходе кроме как тихо выкинуть?
Цитата Сообщение от gng Посмотреть сообщение
но патч prik будет в любом случае востребован.
Думаю, что хороший патч кроме этого должен вообще отключать всю цепочку с fflush() для FILE* открытых только для чтения. Но там пляски со сбросом буферов тесно связаны с освобождением памяти - страшно трогать.
2
590 / 413 / 135
Регистрация: 02.10.2008
Сообщений: 1,745
Записей в блоге: 1
15.12.2018, 20:15 19
К стати это не оно?
https://www.linux.org.ru/forum/general/12361599
https://sourceware.org/bugzill... i?id=20568
0
724 / 224 / 72
Регистрация: 01.03.2011
Сообщений: 629
15.12.2018, 20:30 20
Цитата Сообщение от drfaust Посмотреть сообщение
К стати это не оно?
По симптомам и трасам именно оно.
Печально, что в багзиле баг открыт с 16го кода и авторам пох
1
15.12.2018, 20:30
IT_Exp
Эксперт
87844 / 49110 / 22898
Регистрация: 17.06.2006
Сообщений: 92,604
15.12.2018, 20:30
Помогаю со студенческими работами здесь

Ошибка: "Unhandled exception: Stack cookie instrumentation code detected a stack-based buffer overrun"
Не могу понять почему значение ChoiceOfPlayer меняется и почему NumberOfRow и NumberOfColumn всегда...

[bcc32 Error] File1.cpp(19): E2316 'Stack<T>::Stack()' is not a member of 'Stack<T>'
Возникает ошибка File1.cpp(19): E2316 'Stack&lt;T&gt;::Stack()' is not a member of 'Stack&lt;T&gt;' ...

Есть ли связь между STL-stack и stack - высокопроизводительная память?
Куча источников, как всегда много отсебятины, в общем я запутался...

error C2440: 'return' : cannot convert from 'stack<X>' to 'stack<X> *'
Пишу класс PersonKeeper, в нем readPersons(должен считывать информацию о людях из входного потока...


Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
20
Ответ Создать тему
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2024, CyberForum.ru