Thread overview
Güven kontrole mani değildir (Trust but verify)
Aug 01, 2021
Salih Dincer
Aug 02, 2021
Ali Çehreli
Sep 05, 2021
Salih Dincer
August 01, 2021

Merhaba,

Unutmadan yazmalıyım; bugün Zoom ile yaptığımız D buluşmasında şu @safe ve @trusted mevzuları konuşuldu. Devam etmeden önce lütfen bakınız:

https://dlang.org/spec/function.html#function-safety

Ali hocam ayrıntılı bilgi verecektir. Ben bu konuyu önemsediğim için, toplantıda yazılan bir kod üzerinden 2 denemeyi buraya naklediyorum. Aslında ilkini (yani tersini) denememiştik, koddan sonra açıklayacağım:

import std.stdio;

@safe:
  void küyükSayı (ref ubyte k) {
    auto p = () @trusted {
      return cast(uint*)&k;
    } ();
    *p = 300;
  }
  // *imla hatası kolaylık için yapıldı: kü*ük
  void büyükSayı (ref uint b) {
    auto p = () @trusted {
      return cast(ubyte*)&b;
    } ();
    ++(*p);
  }

void main()
{
  uint i = ubyte.max - 1; // uint içinde 254 var,

  i.büyükSayı;            // bu bir büyük sayı...
  assert(i == ubyte.max); // ama 255 oldu: NORMAL

  i.büyükSayı;            // şimdi 256 olmalıydı
  assert(i == ubyte.min); // ama 0 oldu: NASIL?

  ubyte n;          // 0 ile ilklenmiş bir uybte

  n.küyükSayı;      // ubyte.max üstünde bir değer
  n.writeln;        // atadık ama bende 44 çıktı!
} /* ÇIKTISI:
44
* Evet bir çift sayı ve bu bende çıktı! Muhtemelen
* sizde farklıydı ama 300 olmalıydı. İşler karıştı
* çünkü güvenilir (trusted) yazınca D'yi kandırdı!
* Gökten 300 elma düşmüş...:)
*/

Ali hocam, küyük/büyük işlevlerine benzeyen bir foo() yazmıştı ve değerleri aynı şekilde dönüştürüp 42'ye eşitlemişti. Ben ise işaretsiz (uint) tahsis ederek 300 değerini almasını istedim. Tek fark int yerine uint ve 42 yerine 300 o kadar!

Ama 44 gibi absürt bir sonuç çıktı çünkü ayıp bir iş yaptık, sağ gösterip (trusted) sol vurduk (left stranded) yoksa yine her şey normal...

İlk örneği ise denemedik! Bu sefer bellekte ubyte'a göre ferah bir alan (uint) verdim ve cast edilen ile maniple edileni yine işaretsiz olmasına çalıştım. Bu yüzden 255'den sonra sanki büyük sayı değilmiş gibi 0'a geri döndü. Yani ubyte gibi davrandı ama bu sonuç sizde farklı çıkabileceği gibi yine güvenilir değildir.

(Unutmadan son bir not daha): -O ile derlemeyi denemelisiniz, o zaman farklı sonuç alıyorsunuz.

Başarılar...

August 01, 2021
On 8/1/21 10:11 AM, Salih Dincer wrote:

> @safe:
>    void küyükSayı (ref ubyte k) {
>      auto p = () @trusted {
>        return cast(uint*)&k;
>      } ();
>      *p = 300;
>    }
>    // *imla hatası kolaylık için yapıldı: kü*ük
>    void büyükSayı (ref uint b) {
>      auto p = () @trusted {
>        return cast(ubyte*)&b;
>      } ();
>      ++(*p);
>    }
>
> void main()
> {
>    uint i = ubyte.max - 1; // uint içinde 254 var,
>
>    i.büyükSayı;            // bu bir büyük sayı...
>    assert(i == ubyte.max); // ama 255 oldu: NORMAL
>
>    i.büyükSayı;            // şimdi 256 olmalıydı
>    assert(i == ubyte.min); // ama 0 oldu: NASIL?

büyükSayı() içindeki isimsiz işlevinde *p ifadesinin türü 'ubyte' olduğundan, ++(*p) yapıldığında 255'ten sonra 0 değerine dönüşüyor. O 0 değeri de main içindeki i'nin ilk baytına yazılıyor. Eğer i'nin değeri 255'ten daha büyük olsaydı, diğer baytları da 0'dan farklı olurdu ve burada i'nin değeri ubyte.min olmazdı.

>    ubyte n;          // 0 ile ilklenmiş bir uybte
>
>    n.küyükSayı;      // ubyte.max üstünde bir değer
>    n.writeln;        // atadık ama bende 44 çıktı!
> } /* ÇIKTISI:
> 44

küyükSayı, n'nin bulunduğu yere sanki orada bir uint varmış gibi 4 bayt yerleştirir. Hepimizin bilgisayarları küçük soncul (little endian olduğundan), bu dört bayt 44, 1, 0, 0'dır. (1 * 256 + 44). O yüzden bu "tanımsız davranış" hepimizde bence aynı sonucu verecektir.

Bellekte n'den sonraki yere 1, 0, ve 0 yazılmış olması bu programda kötü bir sonuç doğurmuyor. Başka programlarda kötü sonuç doğurabilirdi.

Konuyu tamamlamış olmak için dün yazdığım program da şuydu:

@safe:

void foo(ref ubyte u) {
  auto p = () @trusted {
    return cast(int*)&u;
  }();
  *p = 42;
}

import std.stdio;

void main() {
  ubyte u = 7;
  ubyte x = 8;
  foo(u);
  writeln(x);
}

foo() işlerken main içindeki x'in üzerine de yazılmış olabilir. (Yazılmadığı bir durumu programı -O seçeneği ile derleyince gördük.)

Ali

Not: Ucundan ilgili olarak, Salih dün bir de aslında bambaşka anlamları olan "safe" ve "trusted"ın Türkçe'de yakın olduklarına dikkatimizi çekti: Sırasıyla "güvenli" ve "güvenilir".

Farklarını şöyle bir örnekle gösterebiliriz. Emniyet kemerleri, hasara dayanıklılığı, vs. güvenlik sistemleri bulunan bir araç "safe"tir. Çocukları tarlanın bir ucundan öteki ucuna bu araçla gönderebiliriz. Öte yandan, elli senedir tek kaza yapmamış olan Ragıp amcanın kullandığı traktörün üstünde de gönderebiliriz. Bu ikinci durum kesinlikle "safe" değildir ama "trusted"dır çünkü Ragıp amcaya güveniyoruz.


September 05, 2021

On Monday, 2 August 2021 at 03:55:20 UTC, Ali Çehreli wrote:

>

On 8/1/21 10:11 AM, Salih Dincer wrote:
Not: Ucundan ilgili olarak, Salih dün bir de aslında bambaşka anlamları olan "safe" ve "trusted"ın Türkçe'de yakın olduklarına dikkatimizi çekti: Sırasıyla "güvenli" ve "güvenilir".

Farklarını şöyle bir örnekle gösterebiliriz. Emniyet kemerleri, hasara dayanıklılığı, vs. güvenlik sistemleri bulunan bir araç "safe"tir. Çocukları tarlanın bir ucundan öteki ucuna bu araçla gönderebiliriz. Öte yandan, elli senedir tek kaza yapmamış olan Ragıp amcanın kullandığı traktörün üstünde de gönderebiliriz. Bu ikinci durum kesinlikle "safe" değildir ama "trusted"dır çünkü Ragıp amcaya güveniyoruz.

Hocanın verdiği "Ragıp Amacının Traktörü" örneği çok iyi olduğu için ve 5 Eylül toplantısında (ben buna kısaca Zoom5E21 kodunu veriyorum...) da değindiğimizden, şu leziz örneği istafeleriniz sunarım:

Bir güvenli (safe) ana programımız olsun: @safe void main(){ .. }

Eğer bunun içinde aşağıdaki gibi güvenlir olmayan bir işlem (bellekte ayrılan değişkenlerin birbirlerinin alanına girmesi) yaparsanız tutarsız bir durum oluşur.

@safe void main(){
	struct S {
		align(8): // yaslamayı 8 bit'den küçük bir değer (4, 2 veya 1) yapın!
		int j, i = 42;
	} S s;

	long d = () @trusted {

		auto p =  cast(long*)&(s.i);
		// p bir gösterge (işaretçi) ve aslında long türünde i'ni adresi
		return *p;
	}();
	
	assert(d == 42);
}

Bu kod, @safe'e rağmen her halükarda derlenecektir. Üstelik sonlanmadan çalışacaktır da çünkü yapı içindeki yaslamayı (align) doğru yaptık. Ayrıca derlensin diye güvenilir (@trusted) olarak işaretledik.

Ama kod içindeki yaslama değerini düşürünce birbirlerinin alanına girecek derlendikten sonra exception (Assertion failure) atacak. Özetle ne kadar güvenilir olursa olsun babana bile güvenme be kardeşim. :)

Sevgiler, saygılar...