Форум программистов, компьютерный форум, киберфорум
C для начинающих
Войти
Регистрация
Восстановить пароль
Карта форума Темы раздела Блоги Сообщество Поиск Заказать работу  
 
 
Рейтинг 4.92/25: Рейтинг темы: голосов - 25, средняя оценка - 4.92
0 / 0 / 0
Регистрация: 02.08.2011
Сообщений: 19
1

Утечка памяти в функции

24.10.2012, 04:38. Показов 5053. Ответов 28
Метки нет (Все метки)

Author24 — интернет-сервис помощи студентам
Сама суть
Есть fcgi-приложение которое парсит полученные get-данные и потом производит выборку по MySQL.
В приложении есть функция, через которую утекает память, остальные отключал утечка продолжалась, отключил эту - утечки нет.
Вот сама функция
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
char * GET_(char *s, char *gete){
  char query[100000], url[100000], *ret_1;
  int i = 0, flag = 0;
  sprintf(url, "&%s", gete);
  sprintf(query, "&%s=", s);
  if(strstr(url, query) == '\0')
    return "";//если такого get не существует
  ret_1 = strstr(url, query);
  for(i = 0;;i++){
    if(ret_1[i] == '&' || ret_1[i] == '\0'){
      flag = i;
      break;
    }
  }
  ret_1 = strstr(ret_1, "=");
  for(i = 0;;i++){
    if(ret_1[i] == '&' || ret_1[i] == '\0'){
      flag = i;
      break;
    }
  }
  for(i = 0;;i++){
    if(ret_1[i] == '&' || ret_1[i] == '\0'){
      ret_1[i] = '\0';
      break;
    }else if(ret_1[i] == '='){
      continue;
    }else{
      ret_1[i-1] = ret_1[i];
    }
  }
  ret_1[i-1] = '\0';
  return ret_1;
}
Где char *s нужный get в запросе (например "local"), а char *gete это переданный getenv("QUERY_STRING")
Пробовал с использованием strtok - эффект тот же - утечка.
Все мозги себе сломал, откуда уткает.
OC Linux, сервер Lighttpd
Пожалуйста, помогите.
0
Programming
Эксперт
94731 / 64177 / 26122
Регистрация: 12.04.2006
Сообщений: 116,782
24.10.2012, 04:38
Ответы с готовыми решениями:

Вектор, утечка памяти, функция создания и выделение памяти
Здравствуйте. Есть проблема. функция malloc выделяет память лишь в функции CreateVector(), и при...

Утечка памяти
Как полностью освободить память? Утекает 39 байт int ft_badend(char ***arr, int k) { int i;...

Утечка памяти в free()
Приветствую. Стоит задача вывести заданное количество символов из последовательности Фибоначчи....

Будет ли утечка памяти?
Есть такая функция: struct Item* addItem(struct Item *,int*); В main я ее вызываю следующим...

28
Неэпический
17870 / 10635 / 2054
Регистрация: 27.09.2012
Сообщений: 26,737
Записей в блоге: 1
24.10.2012, 05:08 2
В этой функции нет операций работы с памятью, но есть кое что иное.
Сначала Вы ищете подстроку в буфере
C++
1
ret_1 = strstr(url, query);
затем работаете с этой подстрокой, а затем возвращаете указатель на подстроку в буфере.
Этот буфер, будучи локальным уничтожится после выполнения функции и Ваш возвращенный указатель будет указывать в никуда.

P.S. Может, я конечно, что-то упустил, но вроде так
0
0 / 0 / 0
Регистрация: 02.08.2011
Сообщений: 19
24.10.2012, 05:16  [ТС] 3
Если я Вас правильно понял, то вызов этой функции следующий
C
1
char *get = strdup(GET_("get"));
Что вникуда, я понял когда вызвал эту функцию несколько раз подряд без strdup
Вопрос, если все хорошо и ничего не выделется и освобождается, откуда утечка?
Уже перепробывал на все локальные переменные free, ничего кроме ошибки не получил, оно и понятно, память ведь не выделялась
0
Неэпический
17870 / 10635 / 2054
Регистрация: 27.09.2012
Сообщений: 26,737
Записей в блоге: 1
24.10.2012, 05:21 4
а потом удаляете строку, созданную strdup?
C++
1
free(get);
1
0 / 0 / 0
Регистрация: 02.08.2011
Сообщений: 19
24.10.2012, 05:29  [ТС] 5
Вот я лох!
Я-то делал
C
1
get = "";
Теперь понятно почему после тысячи запросов к серверу шло медленное но верное накопление на килобайты.
Большущее спасибо!
0
Croessmah
24.10.2012, 05:30
  #6

Не по теме:

То есть функция GET_ зря отхватила и была отругана понапрасну?

0
0 / 0 / 0
Регистрация: 02.08.2011
Сообщений: 19
24.10.2012, 05:37  [ТС] 7
Получается, так)))
Недаром задал вопрос в теме для новичков.
К тому же раньше и сейчас пишу на php, а там проблем с памятью нет, хотя и возможности существенно ограничены например с той же работой с БД (нет постоянных соединений)
Си выбрал полазив по инету, что он легче чем си++ и ненамного быстрее.
0
Evg
Эксперт CАвтор FAQ
21280 / 8302 / 637
Регистрация: 30.03.2009
Сообщений: 22,659
Записей в блоге: 30
24.10.2012, 09:37 8
У тебя в функции два массива по 100 килобайт. Это как бы тоже лишняя трата памяти плюс нехорошая нагрузка на операционную систему, потому что сразу появляется несколько страниц в памяти, которые, в основном, не используются. При этом образуются сразу две дырки (два хвоста двух массивов)

Вместо двух массивов лучше использовать alloca, которая выделяет память в стеке. Хуже от этого точно не будет, правда будут лишние вызовы strlen'а

На крайний случай вместо двух массивов вполне можно оставить один, что сократит количество дырок до одной. Вместо:

C
char query[100000], url[100000];
sprintf(url, "&%s", gete);
sprintf(query, "&%s=", s);
сделать что-то типа

C
char buff[100000], *query, *url;
int len;
url = buff;
len = sprintf (url, "&%s", gete);
query = buf + len + 1;
sprintf (query, "&%s=", s);
Весь остальной код остаётся неизменным. В итоге обе строки засядут в один и тот же массив плотно друг за другом и между ними дырки не будет, как в случае с двумя массивами

Добавлено через 3 минуты
C
strstr(ret_1, "=")
следует заменить на

C
strchr(ret_1, '=')
что тоже немного ускорит работу
1
0 / 0 / 0
Регистрация: 02.08.2011
Сообщений: 19
24.10.2012, 13:00  [ТС] 9
Спасибо, Жень!
Только для меня сейчас все это темный лес (стеки, alloca)
Понял насчет
C
1
strchr(ret_1, '=')
Насчет всего остального - как-то не очень
Буду разбираться дальше!
Кстати, а что за две дырки? Речь идет об уязвимости для хакеров, которые через get-запрос смогут причинить ущерб работе приложения?
Думаю (не уверен), лишние strlen'ы ни к чему, сейчас работа этого fastcgi в испытании sieg'ой 350 одновременных запросов за 10 секунд выдает 3,5-3,8 тысячи ответов 200 OK, зато нагрузка на процессор 15-16%, что очень много при весе в оперативке 14 килобайт.
Буду думать как уменьшить нагрузку на проц, чтобы запустить этих fcgi больше 1 процесса, не потеряв при этом в производительности.
0
Evg
Эксперт CАвтор FAQ
21280 / 8302 / 637
Регистрация: 30.03.2009
Сообщений: 22,659
Записей в блоге: 30
24.10.2012, 13:58 10
Цитата Сообщение от Leоn Посмотреть сообщение
Кстати, а что за две дырки?
Дырки в памяти. Т.е. у тебя выделен массив на 100 килобайт, а в реальности туда пишется несколько десятков или сотен байт. Оставшиеся 99 килобайт по сути дела процесс отожрал, но не использует. Если помножить на 350 параллельных процессов, то получается 35 лиших мегабайт плюс дополнительная нагрузка на подсистему памяти, которая обслуживает неиспользуемые страницы памяти

Цитата Сообщение от Leоn Посмотреть сообщение
Только для меня сейчас все это темный лес (стеки, alloca)
Значит вернись к этому вопросу потом, когда более-менее с Си освоишься

Кстати, вызов strdup - это лишнее действие (и последующий вызов free тоже), поскольку функция GET_ у тебя входные строки не портит, а только читает (а потому нет надобности в их дублировании). Ну и для подстраховки (от собственных ошибок) нужно налепить квалификаторы const. Вместо:

C
GET_(char *s, char *gete)
следует написать

C
GET_(const char *s, const char *gete)
Далее, у тебя указатель ret_1 смотрит вовнутрь локального массива. При этом указатель ты возвращаешь наружу. А локальный массив после возврата из функции фактически умирает и работа с таким указателем становится некорректной. Т.е. программа под виндой у тебя бы точно сдохла. Линукс он в этом отношении более разгильдяйский, а потому с такой некорректной памятью работа ещё допускается, но в один прекрасный момент оно по любому сдохнет

Самый быстрый способ пролечить ситуацию - сделать массивы статическими, а не динамическими. Заменить:

C
char query[100000], url[100000], *ret_1;
на

C
static char query[100000], url[100000];
char *ret_1;
А ещё лучше - покажи весь код программы
0
1259 / 650 / 44
Регистрация: 06.02.2011
Сообщений: 1,654
24.10.2012, 14:11 11
Цитата Сообщение от Evg Посмотреть сообщение
сделать что-то типа
Проще вызывать ф-цию сразу как GET_("&name=",) тогда одно копирование просто выкидываем.
Цитата Сообщение от Evg Посмотреть сообщение
Весь остальной код остаётся неизменным.
Не он просто проситься на помойку начиная от бессмысленных переменных (flag), циклов, и т.п. и заканчивая алгоритмом.

2TC: если эта ф-ция вызывается более одного раза на запрос (что логично), то можно сразу в месте где Вы делаете getenv() (и я надеюсь, проверяете и декодируете запрос) заменить все '&' на '\0' и запомнить общую длину запроса - это позволит достать любую переменную за 1 цикл.
Еще вариант, сразу построить вектор типа argv.
0
Evg
Эксперт CАвтор FAQ
21280 / 8302 / 637
Регистрация: 30.03.2009
Сообщений: 22,659
Записей в блоге: 30
24.10.2012, 14:42 12
Цитата Сообщение от g_u_e_s_t Посмотреть сообщение
Проще вызывать ф-цию сразу как GET_("&name=",) тогда одно копирование просто выкидываем
Дык в том-то и дкло, что в точке вызова лишнего амперсанда у него нет (подаёт-то он наверняка не константную строку, а переменную)

Цитата Сообщение от g_u_e_s_t Посмотреть сообщение
он просто проситься на помойку
С учётом того, что у автора нет богатого опыта работы на Си, тут всё нормально
0
1259 / 650 / 44
Регистрация: 06.02.2011
Сообщений: 1,654
24.10.2012, 14:58 13
Цитата Сообщение от Evg Посмотреть сообщение
Дык в том-то и дкло, что в точке вызова лишнего амперсанда у него нет
Дык надо дописать
Цитата Сообщение от Evg Посмотреть сообщение
(подаёт-то он наверняка не константную строку, а переменную)
Вряд ли. В 99% случаев имена нужных GET параметров как-раз известны заранее.
Цитата Сообщение от Evg Посмотреть сообщение
С учётом того, что у автора нет богатого опыта работы на Си, тут всё нормально
Решать конечно не нам, а автору, но по-моему это выглядит страшно.
0
0 / 0 / 0
Регистрация: 02.08.2011
Сообщений: 19
24.10.2012, 15:06  [ТС] 14
Цитата Сообщение от Evg Посмотреть сообщение
Кстати, вызов strdup - это лишнее действие (и последующий вызов free тоже), поскольку функция GET_ у тебя входные строки не портит, а только читает (а потому нет надобности в их дублировании). Ну и для подстраховки (от собственных ошибок) нужно налепить квалификаторы const. Вместо:
Хорошо, а как тогда сохранять значение в переменной функции main?

Цитата Сообщение от Evg Посмотреть сообщение
Самый быстрый способ пролечить ситуацию - сделать массивы статическими, а не динамическими. Заменить:

C
char query[100000], url[100000], *ret_1;
на

C
static char query[100000], url[100000];
char *ret_1;
Спасибо


Цитата Сообщение от Evg Посмотреть сообщение
Дырки в памяти. Т.е. у тебя выделен массив на 100 килобайт, а в реальности туда пишется несколько десятков или сотен байт. Оставшиеся 99 килобайт по сути дела процесс отожрал, но не использует. Если помножить на 350 параллельных процессов, то получается 35 лиших мегабайт плюс дополнительная нагрузка на подсистему памяти, которая обслуживает неиспользуемые страницы памяти
Спасибо, не 350 не параллельных процессов, процесс один, а запросов к fcgi, siege - прога для тестирования серверов.


Цитата Сообщение от Evg Посмотреть сообщение
А ещё лучше - покажи весь код программы
Исходного кода под рукой нет, но если по памяти - то обычный fcgi - читает get, декодирует, обрабатывает mysql_real_escape_string, сравнивает с записями в таблице БД и выдает результат.



Цитата Сообщение от g_u_e_s_t Посмотреть сообщение
Проще вызывать ф-цию сразу как GET_("&name=",) тогда одно копирование просто выкидываем
Стыдно признаться, так я с самого начала и делал, но потом решил немного усложнить задание и чтобы было некоторое удобство при пользовании функцией.
В общем учиться, учиться и еще раз учиться
0
1259 / 650 / 44
Регистрация: 06.02.2011
Сообщений: 1,654
24.10.2012, 15:21 15
Цитата Сообщение от Leоn Посмотреть сообщение
Стыдно признаться, так я с самого начала и делал, но потом решил немного усложнить задание и чтобы было некоторое удобство при пользовании функцией.
Зря наверное

Вот такая кривулька родилась:
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
env_getvar(*name, int *lenp)
{
        size_t len;
        u_int i;
        char *ptr;
 
        if ((ptr = getenv(name)) == NULL)
                return (NULL);
        /* тут нужно декодировать %xx */
 
        if (!(len = strlen(ptr)))
                return (NULL);
 
        if (lenp != NULL)
                *lenp = len;
 
        for (i = 0; i < len; i++) {
                if (ptr[i] == '&')
                        ptr[i] = '\0';
                /* тут хорошо бы проверить на допустимые символы. */
        }
 
        return (ptr);
}
 
char *
query_getarg(const char *name, char *arg, int len)
{
        char *ptr = arg;
        size_t namelen, ptrlen;
        int i;
 
        namelen = strlen(name);
 
        for (i = 0; i < len; i++) {
                if (arg[i] == '\0')
                        continue;
                ptr = arg + i;
                ptrlen = strlen(ptr);
                if (namelen >= ptrlen)
                        continue;
                if (strncmp(name, ptr, namelen) == 0)
                        return (ptr + namelen);
        }
 
        return (NULL);
}
звать так:
C
1
2
3
4
5
6
int qlen;
char *query, *foo, *bar;
if (!(query = env_getvar("QUERY_STRING", &qlen)))
  /* ошибка */;;
if (!(foo = query_getarg("foo=", query, qlen)))
  /* нету или пустое значение у "foo" в запросе */;;
0
0 / 0 / 0
Регистрация: 02.08.2011
Сообщений: 19
24.10.2012, 15:30  [ТС] 16
Я слабак в Си и еще не спал ночь, так что могу ошибиться
Но почему в env_getvar нужно преждевременно декодировать строку? По-моему тут что-то неправильно. По идее сначала смотрим все QUERY_STRING без декодирования, выбираем из него все %xx грубо говоря, а потом их декодируем. А то пользователь может ввести "&" и тогда мы будем иметь неполный запрос.
Может я и ошибаюсь, не знаю.
Заранее приношу извинение за возможную глупость в моем высказывании. Просто мозги уже не варят.
0
1259 / 650 / 44
Регистрация: 06.02.2011
Сообщений: 1,654
24.10.2012, 15:41 17
Leоn, Си как таковой тут абсолютно не причем... Про '&' или например %00 в запросе - решать Вам как правильнее поступить в Вашем случае.
0
0 / 0 / 0
Регистрация: 02.08.2011
Сообщений: 19
24.10.2012, 15:53  [ТС] 18
А можете, если не трудно, меня просвятить, в чем Ваш код лучше-быстрее моего?
Снижена нагрузка на процессор, или увеличена скорость обработки запросов?
Просто интересно
0
1259 / 650 / 44
Регистрация: 06.02.2011
Сообщений: 1,654
24.10.2012, 16:07 19
Leоn, а вот хз - померьте если интересно
Если серьёзно, то Вы не там что-то оптимизировать пытаетесь. Влияние копирования, не как не используемой flag, не нужные циклы и прочего в сумме на производительность/нагрузку стремиться к нулю - Не там ищите. Расставьте замеры времени по коду или погоняйте через профайлер. Не видя кода сказать что-то конретное сложно, но обычно большую часть времени такие вещи тратят на общение с sql.

Добавлено через 4 минуты
Кстати про %26 и подобные коды в запросе вспомнилось - на предыдущем месте работы было принято _все_ текстовые данные отправляемые клиентом заворачивать в base64 (причем не индексируемые поля так и шли в базу - не тратим время на экранирование/фильтрацию). Довольно удобно, имхо.
0
0 / 0 / 0
Регистрация: 02.08.2011
Сообщений: 19
24.10.2012, 16:27  [ТС] 20
Не, у меня к сожалению base64 не прокатит
А Вы случайно не знаете, [ТС] (то что на моем профиле) на этом форуме что значит?
0
24.10.2012, 16:27
IT_Exp
Эксперт
87844 / 49110 / 22898
Регистрация: 17.06.2006
Сообщений: 92,604
24.10.2012, 16:27
Помогаю со студенческими работами здесь

Генератор случайного пароля. Утечка памяти
Написал программу для генерации случайного пароля/ключа. #include &lt;stdio.h&gt; #include &lt;stdlib.h&gt;...

Утечка памяти, невозможно обнаружить её самостоятельно
#define _POSIX_C_SOURCE 200809L #include &lt;stdio.h&gt; #include &lt;stdlib.h&gt; #include &lt;string.h&gt;...

Произойдет ли утечка памяти в приведенном фрагменте кода?
#include &lt;stdio.h&gt; #include &lt;stdlib.h&gt; #include &lt;string.h&gt; char * strconcat( char * s, char *...

Утечка памяти в программе, которая считывает и выводит строку
Есть вот такая простая программа, считывающая строку с клавиатуры и выводящая ее. Программа...


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

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