Многопоточность со встроенной сборкой и доступом к переменной переменного тока - PullRequest
3 голосов
/ 18 сентября 2011

Я использую встроенную сборку для создания набора паролей, которые я буду использовать для противодействия данному хешу. Я использовал этот веб-сайт в качестве справочного материала для создания паролей.

Это работает безупречно в однопоточной среде. Он генерирует бесконечное количество увеличивающихся паролей.

Поскольку у меня есть только базовые знания об асме, я понимаю эту идею. GCC использует ATT, поэтому я компилирую с -masm=intel

Во время попытки многопоточности программы я понимаю, что этот подход может не работать.
В следующем коде используются 2 глобальные переменные C, и я предполагаю , что это может быть проблемой.

__asm__("pushad\n\t"
    "mov edi, offset plaintext\n\t" <---- global variable
    "mov ebx, offset charsetTable\n\t" <---- again
    "L1: movzx eax, byte ptr [edi]\n\t"
    "    movzx eax, byte ptr [charsetTable+eax]\n\t"
    "    cmp al, 0\n\t"
    "    je L2\n\t"
    "    mov [edi],al\n\t"
    "    jmp L3\n\t"
    "L2: xlat\n\t"
    "    mov [edi],al\n\t"
    "    inc edi\n\t"
    "    jmp L1\n\t"
    "L3: popad\n\t");

Он дает недетерминированный результат в переменной открытого текста.

Как создать обходной путь, чтобы каждый поток обращался к своей собственной переменной открытого текста? (Если это проблема ...).

Я пытался изменить этот код, чтобы использовать расширенную сборку, но каждый раз терпел неудачу. Вероятно, из-за того, что все учебники используют синтаксис ATT.

Буду очень признателен за любую помощь, так как я застрял на несколько часов: (

Редактировать : Запуск программы с двумя потоками и печать содержимого открытого текста сразу после инструкции asm приводит к:
b
b
d
d
f
f
...

Редактировать2 :

pthread_create(&thread[i], NULL, crack, (void *) &args[i]))
[...]
void *crack(void *arg) {
struct threadArgs *param = arg;
struct crypt_data crypt; // storage for reentrant version of crypt(3)

char *tmpHash = NULL;

size_t len = strlen(param->methodAndSalt);
size_t cipherlen = strlen(param->cipher);

crypt.initialized = 0;

for(int i = 0; i <= LIMIT; i++) {
    // intel syntax      
    __asm__ ("pushad\n\t"
    //mov edi, offset %0\n\t"
    "mov edi, offset plaintext\n\t"
    "mov ebx, offset charsetTable\n\t"
    "L1: movzx eax, byte ptr [edi]\n\t"
    "    movzx eax, byte ptr [charsetTable+eax]\n\t"
    "    cmp al, 0\n\t"
    "    je L2\n\t"
    "    mov [edi],al\n\t"
    "    jmp L3\n\t"
    "L2: xlat\n\t"
    "    mov [edi],al\n\t"
    "    inc edi\n\t"
    "    jmp L1\n\t"
    "L3: popad\n\t");

    tmpHash = crypt_r(plaintext, param->methodAndSalt, &crypt);
    if(0 == memcmp(tmpHash+len, param->cipher, cipherlen)) {
        printf("success: %s\n", plaintext);
        break;
    }
}
return 0;
} 

Ответы [ 2 ]

2 голосов
/ 19 сентября 2011

Поскольку вы уже используете pthreads, другой вариант - преобразовать переменные, изменяемые несколькими потоками, в переменные для потока (данные, относящиеся к потокам).См. pthread_getspecific Страница руководства OpenGroup .Это работает следующим образом:

В главном потоке (перед созданием других потоков) выполните:

static pthread_key_y tsd_key;
(void)pthread_key_create(&tsd_key);    /* unlikely to fail; handle if you want */

, а затем в каждом потоке, где вы используете plaintext /charsetTable переменных (или больше таких), выполните:

struct { char *plainText, char *charsetTable } *str =
    pthread_getspecific(tsd_key);

if (str == NULL) {
    str = malloc(2 * sizeof(char *));
    str.plainText = malloc(size_of_plaintext);
    str.charsetTable = malloc(size_of_charsetTable);
    initialize(str.plainText);          /* put the data for this thread in */
    initialize(str.charsetTable);       /* ditto */
    pthread_setspecific(tsd_key, str);
}
char *plaintext = str.plainText;
char *charsetTable = str.charsetTable;

Или создайте / используйте несколько ключей, по одному на каждую такую ​​переменную;в этом случае вы не получите str контейнер / двойное косвенное / дополнительное malloc.

Синтаксис сборки Intel со встроенным ассемблером gcc, хм, не велик;в частности, указать операнды ввода / вывода непросто.Я думаю, что для того, чтобы использовать механизм pthread_getspecific, вы должны изменить свой код на:

__asm__("pushad\n\t"
    "push tsd_key\n\t"               <---- threadspecific data key (arg to call)
    "call pthread_getspecific\n\t"   <---- gets "str" as per above
    "add esp, 4\n\t"                 <---- get rid of the func argument
    "mov edi, [eax]\n\t"             <---- first ptr == "plainText"
    "mov ebx, [eax + 4]\n\t"         <---- 2nd ptr == "charsetTable"
    ...

Таким образом, он становится свободным от блокировки, за счет использования большего количества памяти (один открытый текст/ charsetTable для каждого потока), а также за счет дополнительного вызова функции (pthread_getspecific()).Кроме того, если вы сделаете вышеупомянутое, убедитесь, что вы free() определите данные каждого потока с помощью pthread_atexit(), иначе вы утечете.

Если ваша функция выполняется быстро, блокировка будетболее простое решение, потому что вам не нужны все накладные расходы на установку / очистку данных, специфичных для потоков;если функция либо медленная, либо вызывается очень часто, блокировка может стать узким местом - в этом случае затраты памяти / доступа для TSD оправданы.Ваш пробег может отличаться.

1 голос
/ 18 сентября 2011

Защитите эту функцию мьютексом за пределами встроенного монтажного блока.

...