Yığın parçalanması tespit edildi


246

A.out dosyamı yürütüyorum. Yürütmeden sonra program bir süre çalışır ve sonra şu mesajla çıkar:

**** stack smashing detected ***: ./a.out terminated*
*======= Backtrace: =========*
*/lib/tls/i686/cmov/libc.so.6(__fortify_fail+0x48)Aborted*

Bunun olası nedenleri neler olabilir ve nasıl düzeltebilirim?


2
Kodunuzun hangi bölümlerinin yığının parçalanmasına neden olduğunu belirleyebilir ve gönderebilir misiniz? O zaman muhtemelen tam olarak neden olduğunu ve nasıl düzeltileceğini gösterebileceğiz.
Bjarke Freund-Hansen

Ben taşma hatası ile eşanlamlı olduğunu düşünüyorum. Örneğin, 5 öğeyi başlatır ve dizi yaparsanız, bu hata 6. öğeyi veya dizinin sınırları dışındaki herhangi bir öğeyi yazmaya çalışırken görünür.
DorinPopescu

Yanıtlar:


349

Burada Yığın Smashing aslında gcc tarafından arabellek taşması hatalarını tespit etmek için kullanılan bir koruma mekanizması nedeniyle oluşur. Örneğin, aşağıdaki snippet'te:

#include <stdio.h>

void func()
{
    char array[10];
    gets(array);
}

int main(int argc, char **argv)
{
    func();
}

Derleyici (bu durumda gcc) bilinen değerlere sahip koruma değişkenleri (kanaryalar olarak adlandırılır) ekler. 10'dan büyük bir girdi dizesi, bu değişkenin bozulmasına neden olarak SIGABRT'nin programı sonlandırmasına neden olur.

Bir fikir edinmek için, -fno-stack-protector derleme sırasında bu gcc korumasını kullanma seçeneğini devre dışı bırakmayı deneyebilirsiniz . Bu durumda farklı bir hata alırsınız, büyük olasılıkla geçersiz bir bellek konumuna erişmeye çalışırken bir segmentasyon hatası olur. -fstack-protectorBir güvenlik özelliği olduğu için sürüm derlemeleri için her zaman açık olması gerektiğini unutmayın .

Programı bir hata ayıklayıcı ile çalıştırarak taşma noktası hakkında bilgi edinebilirsiniz. Valgrind, yığınla ilgili hatalarda iyi çalışmaz, ancak bir hata ayıklayıcı gibi, çökmenin yerini ve nedenini belirlemenize yardımcı olabilir.


3
bu cevap için teşekkürler! Benim durumumda yazmaya çalıştığım değişkeni başlatmamış olduğumu buldum
Ted Pennings

5
Valgrind,
yığınla

7
Bu cevap yanlış ve tehlikeli tavsiyeler veriyor. Her şeyden önce, yığın koruyucuyu kaldırmak doğru çözüm değildir - bir yığın parçalama hatası alıyorsanız, muhtemelen kodunuzda ciddi bir güvenlik açığı vardır. Doğru yanıt, buggy kodunu düzeltmektir . İkincisi, grasGendarme'nin belirttiği gibi, Valgrind'i deneme önerisi etkili olmayacaktır. Valgrind genellikle yığın tahsisli verilere yasadışı bellek erişimini algılamak için çalışmaz.
DW

22
OP bu davranışın olası nedenlerini sorar, cevabım bir örnek ve bunun makul olarak bilinen bir hatayla nasıl ilişkili olduğunu sağlar. Ayrıca, yığın koruyucunun çıkarılması bir çözüm değildir, problem hakkında daha fazla bilgi edinmek için yapılabilecek bir deneydir. Tavsiye aslında hatayı bir şekilde düzeltmektir, valgrind'i işaret ettiğiniz için teşekkürler, cevabımı bunu yansıtacak şekilde düzenleyeceğim.
sud03r

4
@DW yığın koruması bir yayın sürümünde kapatılmalıdır, çünkü ilk önce - yığın smashing mesajı algılanması yalnızca geliştiriciler için bir yardımcıdır; ikinci olarak - bir başvuru henüz hayatta kalma şansına sahip olabilir; ve üçüncü olarak - bu küçük bir optimizasyon.
Hi-Angel

33

Demontaj analizi ile minimal reprodüksiyon örneği

main.c

void myfunc(char *const src, int len) {
    int i;
    for (i = 0; i < len; ++i) {
        src[i] = 42;
    }
}

int main(void) {
    char arr[] = {'a', 'b', 'c', 'd'};
    int len = sizeof(arr);
    myfunc(arr, len + 1);
    return 0;
}

Yukarı Git .

Derleyin ve çalıştırın:

gcc -fstack-protector -g -O0 -std=c99 main.c
ulimit -c unlimited && rm -f core
./a.out

istendiği gibi başarısız olur:

*** stack smashing detected ***: ./a.out terminated
Aborted (core dumped)

Ubuntu 16.04, GCC 6.4.0'da test edilmiştir.

sökme

Şimdi demontaja bakıyoruz:

objdump -D a.out

içeren:

int main (void){
  400579:       55                      push   %rbp
  40057a:       48 89 e5                mov    %rsp,%rbp

  # Allocate 0x10 of stack space.
  40057d:       48 83 ec 10             sub    $0x10,%rsp

  # Put the 8 byte canary from %fs:0x28 to -0x8(%rbp),
  # which is right at the bottom of the stack.
  400581:       64 48 8b 04 25 28 00    mov    %fs:0x28,%rax
  400588:       00 00 
  40058a:       48 89 45 f8             mov    %rax,-0x8(%rbp)

  40058e:       31 c0                   xor    %eax,%eax
    char arr[] = {'a', 'b', 'c', 'd'};
  400590:       c6 45 f4 61             movb   $0x61,-0xc(%rbp)
  400594:       c6 45 f5 62             movb   $0x62,-0xb(%rbp)
  400598:       c6 45 f6 63             movb   $0x63,-0xa(%rbp)
  40059c:       c6 45 f7 64             movb   $0x64,-0x9(%rbp)
    int len = sizeof(arr);
  4005a0:       c7 45 f0 04 00 00 00    movl   $0x4,-0x10(%rbp)
    myfunc(arr, len + 1);
  4005a7:       8b 45 f0                mov    -0x10(%rbp),%eax
  4005aa:       8d 50 01                lea    0x1(%rax),%edx
  4005ad:       48 8d 45 f4             lea    -0xc(%rbp),%rax
  4005b1:       89 d6                   mov    %edx,%esi
  4005b3:       48 89 c7                mov    %rax,%rdi
  4005b6:       e8 8b ff ff ff          callq  400546 <myfunc>
    return 0;
  4005bb:       b8 00 00 00 00          mov    $0x0,%eax
}
  # Check that the canary at -0x8(%rbp) hasn't changed after calling myfunc.
  # If it has, jump to the failure point __stack_chk_fail.
  4005c0:       48 8b 4d f8             mov    -0x8(%rbp),%rcx
  4005c4:       64 48 33 0c 25 28 00    xor    %fs:0x28,%rcx
  4005cb:       00 00 
  4005cd:       74 05                   je     4005d4 <main+0x5b>
  4005cf:       e8 4c fe ff ff          callq  400420 <__stack_chk_fail@plt>

  # Otherwise, exit normally.
  4005d4:       c9                      leaveq 
  4005d5:       c3                      retq   
  4005d6:       66 2e 0f 1f 84 00 00    nopw   %cs:0x0(%rax,%rax,1)
  4005dd:       00 00 00 

objdump'In yapay zeka modülü tarafından otomatik olarak eklenen kullanışlı yorumlara dikkat edin .

Bu programı GDB üzerinden birden çok kez çalıştırırsanız şunu göreceksiniz:

  • kanarya her seferinde farklı bir rastgele değer alır
  • son döngü myfunc, kanarya adresini tam olarak değiştiren şeydir

Kanarya %fs:0x28, şu şekilde açıklandığı gibi rastgele bir değer içeren ayarlanarak randomize edilir :

Hata ayıklama denemeleri

Şu andan itibaren kodu değiştiriyoruz:

    myfunc(arr, len + 1);

yerine:

    myfunc(arr, len);
    myfunc(arr, len + 1); /* line 12 */
    myfunc(arr, len);

daha ilginç olmak.

Daha sonra, suçlu + 1çağrısını sadece tüm kaynak kodunu okumak ve anlamaktan daha otomatik bir yöntemle tespit edip edemeyeceğimizi görmeye çalışacağız .

gcc -fsanitize=address Adres Temizleyici (ASan)

Bu bayrağı yeniden derlerseniz ve programı çalıştırırsanız, çıktı verir:

#0 0x4008bf in myfunc /home/ciro/test/main.c:4
#1 0x40099b in main /home/ciro/test/main.c:12
#2 0x7fcd2e13d82f in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x2082f)
#3 0x400798 in _start (/home/ciro/test/a.out+0x40079

ardından biraz daha renkli çıktı.

Bu sorunlu çizgiyi (12) açıkça saptar.

Bunun kaynak kodu şu adrestedir: https://github.com/google/sanitizers, ancak örnekte gördüğümüz gibi zaten GCC'ye aktarıldı.

ASan ayrıca bellek sızıntıları gibi diğer bellek sorunlarını da algılayabilir: C ++ kodunda / projesinde bellek sızıntısı nasıl bulunur?

Valgrind SGCheck

Gibi başkaları tarafından bahsedilen , Valgrind Bu tür sorunları çözmek de iyi değil.

SGCheck adlı deneysel bir aracı var :

SGCheck, yığın ve global dizilerin taşmalarını bulmak için bir araçtır. Olası yığın biçimleri ve küresel dizi erişimleri hakkındaki bir gözlemden türetilmiş sezgisel bir yaklaşım kullanarak çalışır.

Bu yüzden hatayı bulamadığımda çok şaşırmadım:

valgrind --tool=exp-sgcheck ./a.out

Hata mesajı şu şekilde görünmelidir: Valgrind eksik hatası

GDB

Önemli bir gözlem, programı GDB aracılığıyla çalıştırırsanız veya coredosyayı bundan sonra incelerseniz :

gdb -nh -q a.out core

daha sonra, montajda gördüğümüz gibi, GDB sizi kanarya kontrolünü yapan fonksiyonun sonuna yönlendirmelidir:

(gdb) bt
#0  0x00007f0f66e20428 in __GI_raise (sig=sig@entry=6) at ../sysdeps/unix/sysv/linux/raise.c:54
#1  0x00007f0f66e2202a in __GI_abort () at abort.c:89
#2  0x00007f0f66e627ea in __libc_message (do_abort=do_abort@entry=1, fmt=fmt@entry=0x7f0f66f7a49f "*** %s ***: %s terminated\n") at ../sysdeps/posix/libc_fatal.c:175
#3  0x00007f0f66f0415c in __GI___fortify_fail (msg=<optimized out>, msg@entry=0x7f0f66f7a481 "stack smashing detected") at fortify_fail.c:37
#4  0x00007f0f66f04100 in __stack_chk_fail () at stack_chk_fail.c:28
#5  0x00000000004005f6 in main () at main.c:15
(gdb) f 5
#5  0x00000000004005f6 in main () at main.c:15
15      }
(gdb)

Bu nedenle, bu işlevin yaptığı çağrılardan birinde sorun olması muhtemeldir.

Daha sonra, kanarya ayarlandıktan hemen sonra ilk tek adım ilerleyerek tam olarak başarısız olan çağrıyı bulmaya çalışıyoruz:

  400581:       64 48 8b 04 25 28 00    mov    %fs:0x28,%rax
  400588:       00 00 
  40058a:       48 89 45 f8             mov    %rax,-0x8(%rbp)

ve adresi izlerken:

(gdb) p $rbp - 0x8
$1 = (void *) 0x7fffffffcf18
(gdb) watch 0x7fffffffcf18
Hardware watchpoint 2: *0x7fffffffcf18
(gdb) c
Continuing.

Hardware watchpoint 2: *0x7fffffffcf18

Old value = 1800814336
New value = 1800814378
myfunc (src=0x7fffffffcf14 "*****?Vk\266", <incomplete sequence \355\216>, len=5) at main.c:3
3           for (i = 0; i < len; ++i) {
(gdb) p len
$2 = 5
(gdb) p i
$3 = 4
(gdb) bt
#0  myfunc (src=0x7fffffffcf14 "*****?Vk\266", <incomplete sequence \355\216>, len=5) at main.c:3
#1  0x00000000004005cc in main () at main.c:12

Şimdi, bu bizi doğru rahatsız edici talimatta bırakıyor: len = 5ve i = 4bu özel durumda, bizi suçlu hattına 12 işaret etti.

Ancak, geri izleme bozuk ve bazı çöp içeriyor. Doğru bir geri izleme şöyle görünecektir:

#0  myfunc (src=0x7fffffffcf14 "abcd", len=4) at main.c:3
#1  0x00000000004005b8 in main () at main.c:11

belki de bu yığını bozabilir ve izini görmenizi engelleyebilir.

Ayrıca, bu yöntem kanarya kontrol fonksiyonunun son çağrısının ne olduğunu bilmeyi gerektirir, aksi takdirde ters hata ayıklama kullanmazsanız her zaman mümkün olmayacak yanlış pozitiflere sahip olursunuz .


16

Lütfen aşağıdaki duruma bakın:

ab@cd-x:$ cat test_overflow.c 
#include <stdio.h>
#include <string.h>

int check_password(char *password){
    int flag = 0;
    char buffer[20];
    strcpy(buffer, password);

    if(strcmp(buffer, "mypass") == 0){
        flag = 1;
    }
    if(strcmp(buffer, "yourpass") == 0){
        flag = 1;
    }
    return flag;
}

int main(int argc, char *argv[]){
    if(argc >= 2){
        if(check_password(argv[1])){
            printf("%s", "Access granted\n");
        }else{
            printf("%s", "Access denied\n");
        }
    }else{
        printf("%s", "Please enter password!\n");
    }
}
ab@cd-x:$ gcc -g -fno-stack-protector test_overflow.c 
ab@cd-x:$ ./a.out mypass
Access granted
ab@cd-x:$ ./a.out yourpass
Access granted
ab@cd-x:$ ./a.out wepass
Access denied
ab@cd-x:$ ./a.out wepassssssssssssssssss
Access granted

ab@cd-x:$ gcc -g -fstack-protector test_overflow.c 
ab@cd-x:$ ./a.out wepass
Access denied
ab@cd-x:$ ./a.out mypass
Access granted
ab@cd-x:$ ./a.out yourpass
Access granted
ab@cd-x:$ ./a.out wepassssssssssssssssss
*** stack smashing detected ***: ./a.out terminated
======= Backtrace: =========
/lib/tls/i686/cmov/libc.so.6(__fortify_fail+0x48)[0xce0ed8]
/lib/tls/i686/cmov/libc.so.6(__fortify_fail+0x0)[0xce0e90]
./a.out[0x8048524]
./a.out[0x8048545]
/lib/tls/i686/cmov/libc.so.6(__libc_start_main+0xe6)[0xc16b56]
./a.out[0x8048411]
======= Memory map: ========
007d9000-007f5000 r-xp 00000000 08:06 5776       /lib/libgcc_s.so.1
007f5000-007f6000 r--p 0001b000 08:06 5776       /lib/libgcc_s.so.1
007f6000-007f7000 rw-p 0001c000 08:06 5776       /lib/libgcc_s.so.1
0090a000-0090b000 r-xp 00000000 00:00 0          [vdso]
00c00000-00d3e000 r-xp 00000000 08:06 1183       /lib/tls/i686/cmov/libc-2.10.1.so
00d3e000-00d3f000 ---p 0013e000 08:06 1183       /lib/tls/i686/cmov/libc-2.10.1.so
00d3f000-00d41000 r--p 0013e000 08:06 1183       /lib/tls/i686/cmov/libc-2.10.1.so
00d41000-00d42000 rw-p 00140000 08:06 1183       /lib/tls/i686/cmov/libc-2.10.1.so
00d42000-00d45000 rw-p 00000000 00:00 0 
00e0c000-00e27000 r-xp 00000000 08:06 4213       /lib/ld-2.10.1.so
00e27000-00e28000 r--p 0001a000 08:06 4213       /lib/ld-2.10.1.so
00e28000-00e29000 rw-p 0001b000 08:06 4213       /lib/ld-2.10.1.so
08048000-08049000 r-xp 00000000 08:05 1056811    /dos/hacking/test/a.out
08049000-0804a000 r--p 00000000 08:05 1056811    /dos/hacking/test/a.out
0804a000-0804b000 rw-p 00001000 08:05 1056811    /dos/hacking/test/a.out
08675000-08696000 rw-p 00000000 00:00 0          [heap]
b76fe000-b76ff000 rw-p 00000000 00:00 0 
b7717000-b7719000 rw-p 00000000 00:00 0 
bfc1c000-bfc31000 rw-p 00000000 00:00 0          [stack]
Aborted
ab@cd-x:$ 

Yığını smashing koruyucusunu devre dışı bıraktığımda, "./a.out wepassssssssssssssssss" komutunu kullandığımda olması gereken hiçbir hata algılanmadı.

Yukarıdaki sorunuzu yanıtlamak için, yığın smashing koruyucunuz etkin olduğundan ve programınızda yığın taşması bulunduğundan "** yığın smashing tespit edildi: xxx" mesajı görüntülendi.

Bunun nerede olduğunu öğrenin ve düzeltin.


7

Valgrind kullanarak sorunu hata ayıklamaya çalışabilirsiniz :

Valgrind dağıtımı şu anda altı üretim kalitesinde araç içermektedir: bir bellek hata dedektörü, iki iş parçacığı hata dedektörü, bir önbellek ve dal tahmin profili, bir çağrı grafiği üreten önbellek profili ve bir yığın profili. Ayrıca iki deneysel araç içerir: bir yığın / yığın / genel dizi taşması detektörü ve bir SimPoint temel blok vektör üreteci. Aşağıdaki platformlarda çalışır: X86 / Linux, AMD64 / Linux, PPC32 / Linux, PPC64 / Linux ve X86 / Darwin (Mac OS X).


2
Evet, ancak Valgrind yığın tahsisli arabellek taşmaları için iyi çalışmaz, bu da bu hata mesajının gösterdiği durumdur.
DW

4
Bu yığın dizisi taşma detektörünü nasıl kullanabiliriz ? Detaylandırabilir misin?
Craig McQueen

@CraigMcQueen Valgrind'in deneysel sezgisel SGCheck yığın parçalama dedektörünü minimal bir örnek üzerinde kullanmaya çalıştım: stackoverflow.com/a/51897264/895245 ama başarısız oldu.
Ciro Santilli 法轮功 冠状 病 六四 事件 法轮功

4

Bu, büyük olasılıkla bir Arabellek taşması sonucu yığıntaki bazı değişkenlere yasadışı bir şekilde yazdığınız anlamına gelir .


9
Yığın taşması, yığının başka bir şeye çarpmasıdır. İşte tam tersi: Yığına bir şey çöktü.
Peter Mortensen

5
Pek sayılmaz. Yığının bir kısmı başka bir parçaya parçalanıyor. Bu gerçekten bir tampon taşmasıdır, sadece yığının üstünden değil, yığının başka bir kısmına "sadece".
Bas Wijnen

2

Bunun olası nedenleri neler olabilir ve nasıl düzeltebilirim?

Bir senaryo aşağıdaki örnekte olacaktır:

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

void swap ( char *a , char *b );
void revSTR ( char *const src );

int main ( void ){
    char arr[] = "A-B-C-D-E";

    revSTR( arr );
    printf("ARR = %s\n", arr );
}

void swap ( char *a , char *b ){
    char tmp = *a;
    *a = *b;
    *b = tmp;
}

void revSTR ( char *const src ){
    char *start = src;
    char *end   = start + ( strlen( src ) - 1 );

    while ( start < end ){
        swap( &( *start ) , &( *end ) );
        start++;
        end--;
    }
}

Bu programda, örneğin reverse()böyle bir şeyle çağrı yaparsanız bir String'i veya stringin bir kısmını tersine çevirebilirsiniz :

reverse( arr + 2 );

Dizinin uzunluğunu şu şekilde geçirmeye karar verirseniz:

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

void swap ( char *a , char *b );
void revSTR ( char *const src, size_t len );

int main ( void ){
    char arr[] = "A-B-C-D-E";
    size_t len = strlen( arr );

    revSTR( arr, len );
    printf("ARR = %s\n", arr );
}

void swap ( char *a , char *b ){
    char tmp = *a;
    *a = *b;
    *b = tmp;
}

void revSTR ( char *const src, size_t len ){
    char *start = src;
    char *end   = start + ( len - 1 );

    while ( start < end ){
        swap( &( *start ) , &( *end ) );
        start++;
        end--;
    }
}

Çok iyi çalışıyor.

Ancak bunu yaptığınızda:

revSTR( arr + 2, len );

Şunları elde edersiniz:

==7125== Command: ./program
==7125== 
ARR = A-
*** stack smashing detected ***: ./program terminated
==7125== 
==7125== Process terminating with default action of signal 6 (SIGABRT)
==7125==    at 0x4E6F428: raise (raise.c:54)
==7125==    by 0x4E71029: abort (abort.c:89)
==7125==    by 0x4EB17E9: __libc_message (libc_fatal.c:175)
==7125==    by 0x4F5311B: __fortify_fail (fortify_fail.c:37)
==7125==    by 0x4F530BF: __stack_chk_fail (stack_chk_fail.c:28)
==7125==    by 0x400637: main (program.c:14)

Ve ilk kodda, uzunluğu nedeniyle Böyle arriç kontrol edilir revSTR()ince olan ama ikinci kodda boyunu geçmesi burada:

revSTR( arr + 2, len );

Uzunluk artık söylediğinizde geçtiğiniz uzunluktan daha uzundur arr + 2.

Uzunluğu strlen ( arr + 2 )! = strlen ( arr ).


1
Bu örneği beğendim çünkü getsve gibi standart kütüphane işlevlerine güvenmiyor scrcpy. Daha da küçültebilir miyiz acaba? En azından kurtulmak istiyorum string.hile size_t len = sizeof( arr );. Gcc 6.4, Ubuntu 16.04 üzerinde test edilmiştir. Ben de arr + 2kopya yapıştırma en aza indirmek için başarısız örnek verecekti .
Ciro Santilli 法轮功 冠状 病 六四 事件 法轮功

1

Arabellek taşmalarının neden olduğu yığın bozulmaları. Savunmacı bir şekilde programlayarak onlara karşı savunabilirsiniz.

Bir diziye her eriştiğinizde, erişimin sınırların dışında olmadığından emin olmak için diziden önce bir onay işareti koyun. Örneğin:

assert(i + 1 < N);
assert(i < N);
a[i + 1] = a[i];

Bu, dizi sınırları hakkında düşünmenizi sağlar ve ayrıca mümkünse bunları tetiklemek için testler eklemeyi düşünmenizi sağlar. Bu önerilerin bazıları normal kullanım sırasında başarısız olursa, bunları normal hale getirin if.


0

Bu hata ayıklama kodunu harcadıktan sonra bir yapı * bazı bellek ayırmak için malloc () kullanırken bu hatayı aldım, nihayet tahsis edilen bellek ve daha sonra hata mesajı gitti :) için free () işlevi kullanılır :)


0

Yığın parçalamanın başka bir kaynağı vfork()yerine (yanlış) kullanımıdır fork().

Alt işlemin execve()hedef yürütülebilir dosyaya erişemediği ve çağırmak yerine bir hata kodu döndürdüğü bir durumda hata ayıkladım_exit() .

Çünkü vfork()o çocuğu olurken etmişti aslında hala sadece ebeveynin yığın bozulmasını, ebeveynin işlem alanı içinde yürütülmesi, ancak "aşağı" kod tarafından basılacak teşhis iki farklı setleri neden olurken, bu geri döndü.

Değişen vfork()için fork()sabit problemler hem, hem çocuğun değişen yaptığı returniçin açıklama _exit()yerine.

Ancak çocuk kodu execve(), diğer rutin çağrılarla (bu özel durumda uid / gid'i ayarlamak için) çağrıdan önce geldiğinden, teknik olarak gereksinimleri karşılamıyor vfork(), bu nedenle kullanmak fork()için değiştirmek burada doğrudur.

(Sorunlu returnifadenin aslında böyle kodlanmadığını unutmayın - bunun yerine bir makro çağrıldı ve bu makro genel bir değişkene _exit()veya returnbu değişkene dayalı olduğuna karar verdi . Bu nedenle, alt kodunvfork() kullanıma . )

Daha fazla bilgi için, bkz:

Fork (), vfork (), exec () ve clone () arasındaki fark

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.