레이블이 deep인 게시물을 표시합니다. 모든 게시물 표시
레이블이 deep인 게시물을 표시합니다. 모든 게시물 표시

[컴] GSMA IMEI Database

imei blacklist / IMEI 블랙 리스트 / 도난 당한 폰 / 폰 도난 방지 시스템 / 중고 / 해외에 중고로 판대

GSMA IMEI Database

GSMA 가 International Mobile Equipment Identity Database (IMEI Db) 로 알려진 유일한 시스템을 유지보수한다.

블랙리스트에 올라간 IMEI 들이 GSMA 중앙 IMEI DB 에 제공된다. 그러면 이 DB를 이용해서 통신업자들이 국제, 국내망 여러 네트워크에서 그 기기를 block 할 수 있다.

하지만, 이것은 완전하진 않다. ref.2 에서 이야기하듯이 IMEI 값은 변경이 가능하다.

GSMA IMEI DB 에 기록하는 정보

IMEI 는 15자리의 숫자이며, 모바일 네트워크에서 기기를 구분하기 위해 사용된다.

IMEI DB에는 “3GPP 를 따르는 기기들” 제조사들의 모든 공식적인 기기들의 정보를 기록한다.
  • IMEI 번호 : “3GPP 를 따르는 기기들” 제조사들의 모든 공식적인
  • 제조사 이름
  • 모델이름
  • 주요 네트워크 능력(주파수 대역, 기기타입 등)

References

  1. GSMA IMEI Blacklisting - Services
  2. International Mobile Equipment Identity - Wikipedia

[컴] git rebase 설명

git rebase / 깃 리베이스 /

git rebase

git rebase 는 아래처럼 설명이 된다.

Git - Rebase하기
  1. 실제로 일어나는 일을 설명하자면 일단 두 브랜치가 나뉘기 전인 공통 커밋으로 이동하고 나서
  2. 그 커밋부터 지금 Checkout한 브랜치가 가리키는 커밋까지 diff를 차례로 만들어
  3. 어딘가에 임시로 저장해 놓는다.
  4. Rebase할 브랜치(역주 - experiment)가 합칠 브랜치(역주 - master)가 가리키는 커밋을 가리키게 하고
  5. 아까 저장해 놓았던 변경사항을 차례대로 적용한다.

그림 3-29는 이러한 과정을 나타내고 있다.

다른 vcs 와 연관지어서...

이보다 이해를 돕기 위해 좀 더 근원적인 설명을 해보려 한다.

일단 rebase 의 의미에서 시작하자. rebase 라는 용어는 git 에서 처음사용된 것이 아니다. 이전의 version control software 에서 존재하던 용어이다.

그냥 단순하게 해석하면 "base 를 다시(re)" 만드는 것이다.

그럼 base 가 무엇을 이야기 하는 것일까. 그것은 현재 내가 작업하고 있는 source 다. 그래서 rebase 를 한다는 것은 현재 내가 base 로 사용하던 source 를 새로 다시 받는 것(rebase)이라고 보면 된다.






이것이 결국 git 에서도 같은 방식으로 동작한다.

git rebase 를 하면 현재 내가 branch 를 받은 시점, 즉 내 작업들이 적용되기 전,(commit 을 하기전) 으로 돌아가서, 나의 base 를 새로운 내용이 적용된 base 로 변경한다. 그리고 내가 작업한 내용(commit) 을 적용하게 되는 것이다.

git rebase 는 여기에 더해 훨씬 다양한 기능을 제공한다. 자세한 내용은 git rebase man page 를 확인하자.



[컴][js] redux-saga 동작 분석




redux-saga 동작 분석

위의 예제로 분석할 것이다.

// saga.js
import { put, takeEvery, all  } from 'redux-saga/effects'

const delay = (ms) => new Promise(res => setTimeout(res, ms))


export function* helloSaga() {
  console.log('Hello Sagas!')
}


// ...

// Our worker Saga: will perform the async increment task
export function* incrementAsync() {
  yield delay(1000)
  yield put({ type: 'INCREMENT' })
}

// Our watcher Saga: spawn a new incrementAsync task on each INCREMENT_ASYNC
export function* watchIncrementAsync() {
  yield takeEvery('INCREMENT_ASYNC', incrementAsync)
}



// notice how we now only export the rootSaga
// single entry point to start all Sagas at once
export default function* rootSaga() {
  yield all([
    helloSaga(),
    watchIncrementAsync()
  ])
}


// main.js
import "babel-polyfill"

import React from 'react'
import ReactDOM from 'react-dom'
import { createStore, applyMiddleware } from 'redux'

import Counter from './Counter'
import reducer from './reducers'
//// default export 인 sagaMiddlewareFactory 를 createSagaMiddleware 에 assign 한 것
import createSagaMiddleware from 'redux-saga'
// import { rootSaga } from './sagas'
import rootSaga from './sagas'

// const store = createStore(reducer)
const sagaMiddleware = createSagaMiddleware()
const store = createStore(
  reducer,
  applyMiddleware(sagaMiddleware)
)
sagaMiddleware.run(rootSaga)

const action = type => store.dispatch({type})

function render() {
  ReactDOM.render(
    <Counter
      value={store.getState()}
      onIncrement={() => action('INCREMENT')}
      onDecrement={() => action('DECREMENT')}
      onIncrementAsync={() => action('INCREMENT_ASYNC')} />,
    document.getElementById('root')
  )
}

render()
store.subscribe(render)

createSagaMiddleware()

createSagaMiddleware() --> sagaMiddlewareFactory() 가 호출된다. sagaMiddlewareFactory 를 확인해보면 대체로 이전버전의 사용법에 대한 error 를 던져주기 위한 용도인듯 하다. 그외에는 sagaMiddleware 라는 function 을 만들어서 return 해준다.

sagaMiddleware() 를 호출할 때 runSaga 에 첫번째 argument 를 assign 해준다.(이것이 boundRunSaga) 그리고 sagaMiddleware.run 을 하면,  이 boundRunSaga  를 호출한다.


createStore() / applyMiddleware()

applyMiddleware() 는 단순하다. 기존의 createStore() 를 실행하고, 그 이외에 다른 동작을 하기 위한 함수라고 보면 된다. python 의 decorator 같은 역할이다.

물론 로직은 좀 더 복잡하게 짜여있다. applyMiddleware() 를 직접실행시키는 것이 아니라createStore 내에서 applyMiddleware() 가 decorator 처럼 동작해야 해서 createStore 내에서 다시 호출(call)한다. 그런 이유로 applyMiddleware() 가 function 을 return 한다. 말이 복잡하다. source code 를 확인하자.

export default function applyMiddleware(...middlewares) {
  return createStore => (...args) => { // pass the createStore as an argument
    const store = createStore(...args)
    let dispatch = () => {
      throw new Error(
        'Dispatching while constructing your middleware is not allowed. ' +
          'Other middleware would not be applied to this dispatch.'
      )
    }

    const middlewareAPI = {
      getState: store.getState,
      dispatch: (...args) => dispatch(...args)
    }
   // 기존의 dispatch 에 middleware 를 추가해서 dispatch 를 새롭게 만든다.
    const chain = middlewares.map(middleware => middleware(middlewareAPI))
    dispatch = compose(...chain)(store.dispatch)

    return {
      ...store,
      dispatch
    }
  }
}
...
export default function createStore(reducer, preloadedState, enhancer) {
  ...
  if (typeof enhancer !== 'undefined') {
    if (typeof enhancer !== 'function') {
      throw new Error('Expected the enhancer to be a function.')
    }

    return enhancer(createStore)(reducer, preloadedState)
  }
  ...
}


sagaMiddleware.run(rootSaga)

sagaMiddleware.run(rootSaga) 를 하면 runSaga() 가 호출된다. runSaga() 에서는 필요한 설정들에 대한 값들을 set 하고(sagaMonitor 같은) proc() 를 호출한다.(proc.js)
proc(env, iterator, context, effectId, getMetaInfo(saga), /* isRoot */ true, noop)

proc 에서 stdChannel 를 생성하는데, 이녀석은 약간의 경고문구만 추가한 eventChannel 이다. 이녀석은 createSagaMiddleware 를 할 때 생성된다.

proc 에서 next() 를 호출하고, next 는 iterator.next() 를 호출한다. iterator 를 이용해서 all() 로 묶인 task 들을 한번에 처리한다.


// saga.js
export default function* rootSaga() {
  yield all([
    helloSaga(),
    watchIncrementAsync()
  ])
}
...

// runSaga.js
function runSaga(_ref, saga) {
  ...

  var iterator = saga.apply(void 0, args);
  ...
  return immediately(function () {
    var task = proc(env, iterator, context, effectId, __chunk_1.getMetaInfo(saga),
    /* isRoot */
    true, __chunk_1.noop);

    if (sagaMonitor) {
      sagaMonitor.effectResolved(effectId, task);
    }

    return task;
  });
}

function proc(env, iterator, parentContext, parentEffectId, meta, isRoot, cont) {
  if (iterator[__chunk_1.asyncIteratorSymbol]) {
    throw new Error("redux-saga doesn't support async generators, please use only regular ones");
  }
  ...
  next(); // then return the task descriptor to the caller
  
  return task;
  /**
   * This is the generator driver
   * It's a recursive async/continuation function which calls itself
   * until the generator terminates or throws
   * @param {internal commands(TASK_CANCEL | TERMINATE) | any} arg - value, generator will be resumed with.
   * @param {boolean} isErr - the flag shows if effect finished with an error
   *
   * receives either (command | effect result, false) or (any thrown thing, true)
   */

  function next(arg, isErr) {
    try {
      var result;
      
      if (isErr) {
        result = iterator.throw(arg); // user handled the error, we can clear bookkept values
        clear();
      } else if (__chunk_1.shouldCancel(arg)) {
        /**
          getting TASK_CANCEL automatically cancels the main task
          We can get this value here
           - By cancelling the parent task manually
          - By joining a Cancelled task
        **/
        ...
      } else if (__chunk_1.shouldTerminate(arg)) {
        // We get TERMINATE flag, i.e. by taking from a channel that ended using `take` (and not `takem` used to trap End of channels)
        ...
      } else {
        result = iterator.next(arg);
      }

      ...
    } catch (error) {
      if (mainTask.status === CANCELLED) {
        throw error;
      }

      mainTask.status = ABORTED;
      mainTask.cont(error, true);
    }
  }
  ...
}


redux-saga 사용

redux-saga 는 특정 이벤트가 끝날때에 대한 작업을 미리 등록해 놓을 수 있다.(saga.js)

그래서 원래라면,

  1. 특정 이벤트가 발생하고, 
  2. 그 작업을 수행한 후(event handler)에 
  3. 우리가 원하는 작업을 실행하기 위해서 우리의 code를 추가해야 한다.(callback) 
하지만 redux-saga 는 그부분의 대한 구현에 관계없이 구현을 하면 된다.

redux-saga 를 이용해서 event 가 끝나는 시점에 호출되는 대신에,

  1. 먼저 특정 event 를 기다리는 code 를 실행하고 
  2. event 가 발생할 때 까지 control 을 다른 곳에 넘겨준다. 그리고 event 가 발생하면 다시 control 을 받아온다.(yield)

  yield takeEvery('INCREMENT_ASYNC', incrementAsync)

아래 글을 읽으면 자세한 이야기를 알 수 있다.








[컴][NodeJs] CPU Intensive job 을 Node.js 에서 처리할 때

노드js nodejs 에서 싱글 쓰레드 / single thread job / cpu 사용량이 큰 작업 처리 방법 /

CPU Intensive job 을 Node.js 에서 처리할 때

node js - single thread

기본적으로 single thread 로 알려져 있는데, 이것은 약간 이해를 잘 못 할 수 있어서 간단한 설명을 하자면, ref. 2 에서 여기 를 보면 node js 의 구성을 살짝 엿볼 수 있는데, libeio 를 통해 thread pool 을 만들어서 사용한다. (ref. 3)
그렇기 때문에 이것이 single threaded 인 것은 user 의 source code 를 run 하는 부분을 이야기 한다. 아래 코드를 예로 들면 user 의 code 부분은 하나의 thread 에서 동작한다. 하지만 fs.readFile 같은 부분은 Thread Pool 로 던져질 것이다. 그리고 response 가 오기까지 또 이 single thread 는 console.log('right after') 부분으로 처리할 수 있게 되는 것이다.
var fs = require('fs');  
fs.readFile(‘/files/help.txt’, function(err, buf) {  
    console.log('done');
});
console.log('right after');
from : ref. 1

cpu intensive job

그러므로 nodejs 로 만든 server 들은 기본적으로 single thread 로 user request 를 처리하게 된다. 하지만 만약 request 하나에서 cpu intensive 한 작업을 처리하는 경우가 있는 request 에 대해서는 nodejs 에서 처리가능한 request 양이 급격히 줄어들 수 밖에 없다. 이런 문제점에 대한 해결책에 대한 이야기가 아래 글들에 담겨있다.
대략적인 내용은 cluster 를 사용해서 request 마다 process 를 하나 더 만들어서 cpu intensive job 을 그쪽으로 넘기는 것인데, 문제는 이것이 너무 많은 memory 를 할당해야 한다는 것이다. 그래서 cluster 와 queue(kue library) 를 이용해서 일종의 process pool 을 만들어서 사용하는 해결책을 제시한다.

여담으로, 이 방법들을 살펴보고 있으려니까 아주 오래전에 linux 에서 c 로 server programming 을 하던 때의 생각이 난다.

node js http server timeout

nodejs v6.x 에서 일단 기본 timeout 이 2 분으로 설정되어 있다고 한다. 즉, request 를 보낸지 2분후 까지 response 가 없으면 error 가 된다. 이 timeout 내에 더 많은 request 를 처리하기 위해서라도 "cluster 의 사용"은 필요할 듯 하다.

References

  1. Introduction to NodeJS, A SSJS: Part II - EventLoop Explained
  2. https://youtu.be/L0pjVcIsU6A?t=366
    • 동영상에서는 Node.js 의 철학? 을 알 수 있다.
  3. javascript - Nodejs Event Loop - Stack Overflow
  4. Cluster | Node.js v12.3.1 Documentation

[컴][웹][js] v8 Scanner



from: https://v8.dev/blog/scanner

v8 Scanner

v8 이 소스를 파싱해서 AST 로 만든다.abstract syntax tree (AST)
이걸로 프로그램 구조를 표현한다.

v8은 complile 해야 프로그램을 실행할 수 있다.
v8 parser 은 scanner 에서 만들어놓은 token 을 소모한다.

이때 scanner 가 사용하는 character stream의 encoding 은 utf16만 지원한다.
scanner 에 input 으로 제공하기 전에 이전에 모든 encoding 을 utf16으로 convert 하는 과정을 거친다.

scanner는 이 utf16을 decode 해서 unicode 로 만들어서 사용한다.
이 decoded character 들을 이제 가져다 scanner 가 token 을 만든다.



Whitespace

2개의 token 사이에 whitespace 가 있으면 거기에 ;(semicolon)을 넣는다.(ECMAScript® 2020 Language Specification > Auto semicolon insertion)

다음 token 을 scan 하기 전까지 모든 whitespace는 skip 된다.(newline 이 발생하는지 추적하면서)

real world 에서는 대체로 js 들이 minified 돼서 여러개의 whitespace 가 연속해서 나타나는 경우가 드물다.

그래서 V8 은 whitespace 도 하나의 token 으로 취급한다. (Token::WHITESPACE) 그래서 '//' 같이 comment 가 오는 경우에도 Token::WHITESPACE 를 return 해 버린다.  그러면 paring 하는 loop 에서 다른 Token이 나올때 까지 계속 scan 을 이어나갈 수 있게 된다.

(역주: 다시 말하면, // 가 나와서 이것을 다른 token 으로 정의(Token::COMMENT) 등으로 정의해서 사용하게 되면, loop 을 한번 벗어난 후에 다시 parsing 을 시작해야 하지만, 이것을 whitespace 와 같은 속성으로 취급해 버리면, 이 overhead 를 없앨 수 있게 되는 것이다.)



V8_INLINE Token::Value Scanner::ScanSingleToken() {
  Token::Value token;
  do {
    next().location.beg_pos = source_pos();

    if (V8_LIKELY(static_cast<unsigned>(c0_) <= kMaxAscii)) {
      token = one_char_tokens[c0_];

      switch (token) {
        ...
        
        case Token::DIV:
          // /  // /* /=
          Advance();
          if (c0_ == '/') {
            uc32 c = Peek();
            if (c == '#' || c == '@') {
              Advance();
              Advance();
              token = SkipSourceURLComment();
              continue;
            }
            token = SkipSingleLineComment();
            continue;
          }
          if (c0_ == '*') {
            token = SkipMultiLineComment();
            continue;
          }
          if (c0_ == '=') return Select(Token::ASSIGN_DIV);
          return Token::DIV;
          
        ...

        case Token::WHITESPACE:
          token = SkipWhiteSpace();
          continue;

        case Token::NUMBER:
          return ScanNumber(false);

        case Token::IDENTIFIER:
          return ScanIdentifierOrKeyword();

        default:
          UNREACHABLE();
      }
    }

    if (IsIdentifierStart(c0_) ||
        (CombineSurrogatePair() && IsIdentifierStart(c0_))) {
      return ScanIdentifierOrKeyword();
    }
    if (c0_ == kEndOfInput) {
      return source_->has_parser_error() ? Token::ILLEGAL : Token::EOS;
    }
    token = SkipWhiteSpace();

    // Continue scanning for tokens as long as we're just skipping whitespace.
  } while (token == Token::WHITESPACE);

  return token;
}




Identifier scanning
Internalizing minified identifiers
Keywords
Surrogate pairs
AdvanceUntil



[컴][네트워크] DNS over TLS / DNS over HTTPS

인트라 / 클라우드플레어  / vpn / 우회 / 워닝사이트 / warning / EDNS / Encrypted DNS



only DNS over HTTPS (DoH)

이유는 잘 모르겠지만, 대체로 한번에 DNS query 가 제대로 동작하지 않는 느낌이다. (아마도 응답속도의 문제일 수도 있을 듯 싶다.) 몇번 error 를 뿜어내더라도 계속 시도하면 된다. 그 이후로는 cache 를 사용해서 문제가 없을 듯 싶다.

DNS over TLS (DoT)

접속 Test

https://www.coludflare-dns.com/help : DoH 나 DoT 가 설정됐다면 이 곳에 접속이 가능하다.

Tun2socks 

Test

[컴] 썬더클랩 Thunderclap

썬더볼트 취약점 /


paper : http://thunderclap.io/thunderclap-paper-ndss2019.pdf

썬더클랩(Thunderclap)

  • 이 취약점은 물리적인 접근을 한 공격자에게 Thunderbolt(썬더볼트) 포트를 이용해서 해당기계를 위험하게 한다.
  • 공격자가 가장높은 권한으로 임의의 코드를 실행시킬 수 있게 해준다. 그래서 잠재적으로 암호, 은행 로그인, 암화화 키, 개인파일등에 접근할 수 있게 해준다.
  • 또한, 공격자가 이 취약점을 이용하면 겉으로 무해해 보이는 충전기, 프로젝터 같은 주변기기가 충전이나, 영상을 쏘아 주는 사이에 "host 머신"을 위험하게 할 수 있다.(이러면, 우리가 제3자의 충전기도 마음대로 사용하기 힘들어진다. 마치 공용 wifi 같이)

동작

네트워크 카드의 동작

  1. OS 가 network interface card(NIC) 에 packet 을 보내라고 요청할 때
  2. OS 는 NIC에 보낼 data 에 대한 address 를 제공해 준다.
  3. 그러면 NIC 의 payload 함수는 plaintext data를 찾으려고 memory 근처를 검색할 것이다.


Thunderclap 방법

When an IOMMU is in operation, the most obvious way touse it for protection requires some changes to ring buffer usage. 
  • First,  packet  data  must  be  allocated  from  a  pool  of  physical memory  that  allows  exposure  to  devices  (some  memory  maybe  inaccessible  due  to  hardware  limitations).  
  • Second,  before a data  block  is  placed  in  the  ring  buffer  for  transmission,  a window must be opened for it to be accessible by the device. This involves creating a mapping for the block in the IOMMU page table.
  • Third, the address written into the ring buffer is now the I/O virtual address of the mapping, rather than the physical address. 
  • Finally, when the device is finished with the data, the operating system should close the window again, revoking the mapping from the IOMMU page table and IOTLB.




How do the Thunderclap vulnerabilities work?

The Thunderclap vulnerabilities stem from the fact that computer peripherals such as network cards and GPUs have traditionally been trusted parts of a computer system: they have direct memory access (DMA), which allows them to read and write all of system memory without operating system oversight. DMA allows peripherals to bypass operating system security policies, and DMA attacks abusing this access have been widely employed by hackers and the intelligence community to take control of and exfiltrate sensitive data from target machines. This means passwords, banking logins, private files and browser activity are all exposed, and an attacker can inject any code they wish onto your machine.

Current systems feature input-output memory management units (IOMMUs), protection mechanisms that allow the operating system to restrict peripheral-device memory access. With IOMMU usage enabled, operating systems can protect against DMA attacks by restricting memory access to peripherals that perform legitimate functions and only allowing access to non-sensitive regions of memory. Unfortunately, IOMMU protection is turned off by default in many systems.

Our work leverages vulnerabilities in operating system IOMMU usage to compromise a target system via DMA, even in the presence of an IOMMU that is enabled and configured to defend against DMA attacks. The novel Thunderclap security evaluation platform, built on field-programmable gate array (FPGA) hardware, mimics the functionality of a legitimate peripheral device to convince a target operating system to grant it access to regions of memory. It then examines those regions of memory to find a rich and nuanced attack surface of vulnerable structures that can be exploited to take control of the system.

The rise of hardware interconnects like Thunderbolt 3 over USB-C that combine power input, video output, and peripheral device DMA over the same port greatly increases the real-world applicability of Thunderclap vulnerabilities. Thunderbolt can allow potentially malicious devices to hotplug into a running machine and obtain direct memory access, which makes DMA attacks against temporarily unattended targets feasible. Furthermore, the confusion of power, video, and DMA facilitates the creation of malicious charging stations or projectors that take control of connected machines.

Additionally, our work shows that the Thunderclap vulnerabilities can also be exploited by compromised firmware on existing PCI Express devices, for example network cards or baseboard management controllers (BMCs) integrated into servers. A firmware compromise might be introduced via a firmware vulnerability or a compromise in the device supply chain or factory.


How does this work differ from earlier DMA attacks such as Inception?

Early DMA attacks relied on the absence of an IOMMU. They involved scanning all of a system’s memory for sensitive data from devices that did not appear to the system as legitimate peripherals. These attacks were addressed by the introduction of IOMMUs, which block all memory access from unrecognized devices.

Some previous DMA attacks have taken advantage of weaknesses in IOMMU configuration or setup to disable IOMMU protections. Thunderclap explores serious vulnerabilities that are present even once the IOMMU is configured correctly.

Technical details of the Thunderclap platform

The Thunderclap platform consists of an FPGA that runs the Thunderclap application. The FPGA then plugs into a computer via PCI Express or Thunderbolt. The Thunderclap application makes the FPGA behave to the computer like a genuine Ethernet card (the Intel 82574L network interface card or NIC). The operating system will identify the ethernet peripheral, load drivers, allow the device to access memory (via DMA and an IOMMU if enabled), and ask it to send and receive packets.

With this deep interaction with the operating system, Thunderclap’s device model provides hooks that allow payload functions to be added to device behavior. For example, when the operating system asks the NIC to send a packet, it provides the NIC with the address of the data to send. A payload function might search nearby memory looking for plaintext data that was intended for a different network device.

The Thunderclap application runs on Intel/Altera FPGA boards:
  1. Intel Arria 10 SoC Development Kit ($4500) with Samtec HDR-181157-01-PCIEC cable (available from Samtec direct) - currently recommended
  2. Enclustra Mercury+ AA1 module (ME-AA1-270-3E4-D11E) on PE1 carrier board (~EUR 800) - work in progress
  3. Terasic DE5-Net board (Stratix V) with BERI soft-CPU - no longer supported
  4. As far as we can ascertain, Xilinx, Lattice and Intel Cyclone FPGAs don’t allow us to replace the vendor-supplied implementation of configuration registers with our own (Intel calls it ‘config bypass’ mode) which we require.
It is composed of several pieces:
  1. The underlying FPGA bitfile, containing the hardware that receives PCIe packets (TLPs) and delivers them to software. The FPGA contains an Arm Cortex A9 CPU (hard processor system or HPS) to run our software stack. GitHub repo
  2. The Ubuntu 16.04 operating system running on the on the Arm, including kernel, device tree and u-boot bootloader (which also loads the FPGA bitfile at boot time). Automated build scripts (work in progress): GitHub repo
  3. The Thunderclap application, which is a substantially cut down version of QEMU, based on its e1000e device. This runs in Ubuntu on the ARM core and connects directly to the PCIe queues provided by the hardware. GitHub repo





[컴] iOS 보안-시스템 보안

iOS 부팅 절차 / boot time / booting process / enclave


ref. 1 의 System Security 의 일부를 번역했다. 자세한 내용은 ref. 1을 보도록 하자.

iOS 보안-시스템 보안

기기암호화(device encryption) 는 기본적으로 설정되어 있으며, 설정을 변경할 수 없다.

Secure Boot Chain

  1. iOS 켜기
  2. --> AP 가 Boot ROM 의 code 를 실행
    1. the hardware root of trust 라고 알려져 있다.
    2. the chain of trust 의 첫번째 단계(first step) 이다.
    3. 이 code 는 변경불가능(immutable code)
    4. 칩제조(chip fabrication) 때 만들어 진다.
    5. implicitly trusted
    6. Apple Root CA public key 를 가지고 있다.
      1. 이 public key 는 iBoot bootloader 가 load 되기 전에 Apple 에 의해 서명(sign) 됐다는 것을 확인(verify) 하는데 사용된다.
  3. Boot ROM 에 의해 추가적인 Low-Level Bootloader(LLB) stage 가 load 되고, 확인(verify) 된다.
    1. A9를 포함한 A9 이전의 A시리즈 processor 에만 존재.
  4. 만약 Boot ROM 이 LLB 또는 iBoot 을 load 하는 것을 실패하면,  기기는 DFU mode (Device Firmware Upgrade)로 들어가게 된다.
    1. 오래된 기기들에서 LLB 를 load 하는 것을 실패 하거나,
    2. 새로운 기기들에서 iBoot 을 load 하는 것을 실패하는 것
  5. LLB 나 iBoot 이 다음 작업(next step) 을 load 하거나 verify 하는 것을 실패하는 경우
    1. startup 은 멈춰지고(halt) iTunes 에 접속하라는 화면이 보여진다. 
    2. --> recovery mode 이다.
  6. DFU mode 로 가거나, recovery mode 로 들어가면, USB 를 이용해서 iTunes 에 접속해서 "공장 기본 세팅(factory default settings)" 로 복구되어 져야만 한다.
  7. 기기가 Recovery Mode 와 DFU Mode 로 들어가기 전에 The Boot Progress Register (BPR) 가 update 된다. 이 update 된 값을 보고 Secure Enclave 가 user data 에 대한 접근제한을 하게 된다.
    1. Recovery Mode: A10, S2 그리고 최신의 SoCs 들을 가진 기기에서 iBoot 에 의해 BPR 이 set 된다.
    2. DFU Mode : A12 SoC 를 가진 기기들에서 Boot ROM 에 의해 BPR 이 set 된다.
  8. --> iBoot 이 일들을 마치면
  9. --> iBoot 은 iOS kernel 을 확인(verify) 하고, 실행한다.

baseband subsystem 또한 이것과 비슷한 자신의 secure booting 을 이용한다.
Secure Enclave coprocessor 또한 secure boot process 를 이용하고, 그로인해 그것의 분리되 소프트웨어가 verified and signed by Apple 되었다는 것을 확신시켜준다.


시스템 소프트웨어 권한부여(System Software Authorization)

만약 과거버전의 iOS의 설치가 가능하다면 해커는 과거버전의 iOS를 깔고 권한을 가져올 수 있다. 이것을 막기 위해서 iOS는 System Software Authorization 을 이용한다. Secure Enclave 도 이것을 이용한다.


iOS update

iOS update 는 2가지 방식(iTunes, OTA) 으로 가능하다.

  • iTunes 로 업데이트를 할 때
    • iOS image 전체를 download 
    • Content Caching 옵션을 켠 macOS High Sierra 를 돌리는 Mac 을 사용한다면, 미리 cache 되어 있게 되어서, iOS 의 update 를 바로 할 수 있게 해준다.
  • OTA 로 할 때: patch 를 할 내용만 다운로드한다. 


installation authorization server

iOS update 할때 기기는 Apple 의 installation authorization server 에 접근한다.
그리고

  1. bundle의 measurements: 설치될 설치 bundle들(iBoot, kernel, iOS Image)에 대한 암호화된 measurements
  2. nonce : signed data 를 가져다가 다른 기기에 쓰는 것을 막아주고, system software 를 변경하지 못하게 막는다.
  3. ECID: 기기의 고유한  ECID(Exclusive Chip Identification) 를 서버로 보내게 된다.

installation authorization server 에서 하는일

서버에서는  이 installation bundle 의 버전들이 맞는 구성인지를 확인한다. 예를 들면 특정버전의 iBoot 은 특정 version 의 kernel 을 써야만 하는식이다. 이렇게 버전이 맞으면 이 measurement 에 ECID 를 더해서 sign 을 하고 이 sign 된 data 를 기기에 보내준다.

boot-time verification

위에 설명한 startup process 가 Apple 이 sign 한 code 만 기기에 설치되는 것을 보장하게 해준다.
  • Apple 에게서 온 signature : boot time 에 일어나는 chain-of-trust evaluation 는 Apple 에게서 온 signature 를 verify 한다.
  • disk 에서 load 되는 item 의 measurement: "disk 에서 load 되는 item 의 measurement (기기의 ECID를 이용해서 만들어져 있다.)"와 "signature 에 의해 감싸진 measurement(위의 installation authorization server 에서 보내온)"가 맞는지를 verify 한다.

이 절차들이 특정기기에 대한 authorization 을 해주고, 기기에서 옛 iOS버전이 다른 곳으로 copy 안되도록 한다.


Secure Enclave

  • Secure Enclave 는 보조 프로세서이다. 
  • SoC 안에 들어있다.(fabricated)
  • 암호화된 메모리를 사용
  • 하드웨어 난수발생기(random number generator) 를 포함.
  • Secure Enclave Boot ROM
    • application processor Boot ROM 과 비슷하게, Secure Enclave Boot ROM 은 변경이 불가능한 code (immutable code) 이다. Secure Enclave 를 위한 "하드웨어 root of trust" 를 확립한다.(establish)
    • device 가 시작될 때, Secure Enclave Boot ROM 이 수명이 짧은 메모리 보호 키(ephemeral memory protection key)를 만든다. 
  • Secure Enclave 는 데이터 보호 키 관리에게 (Data Protection key management) 모든 암호화 기능(cryptographic operations)들을 제공
  • 심지어 커널이 허락한 상태에도(kernel has been compromised) Data Protection 의 integrity (데이터가 변경, 파괴되지 않은 상태)을 유지.
  • AP(Application processor) 와 Secure Enclave 사이의 통신은 interrupt-driven mailbox 와 shared memory data buffer 들과 분리되어 있다.

Secure Enclave 는 "L4 마이크로 커널의 Apple-customized version" 에 기초해서 만들어진 Secure Enclave OS 를 실행한다. 이 Secure Enclave OS 는 Apple 에 의해 sign 되고, Secure Enclave Boot ROM 에 의해 verify 되고, 개인의 소프트웨어 업데이트 프로세스를 통해 update 된다.
  • device 가 시작될 때, Secure Enclave Boot ROM 이 수명이 짧은 메모리 보호 키(ephemeral memory protection key)를 만든다. 이 키는 기기의 UID 와 얽히게 만들고(entangle), 기기의 memory space 의 "Secure Enclave 부분"을 암호화 할 때 사용된다.
    • 예외적으로 Apple 의 A7 에선, Secure Enclave memory "memory protection key" 로도 인증(authenticated) 이 가능하다. A11과 A11 이후 그리고 S4 SoC 들에서는 "memory protection key" 와 on-chip SRAM 에 저장된 nonce 들에 의해 인증된다.(authenticated)
    • 그리고, A11과 A11 이후 그리고 S4 SoC 들에서 integrity tree 는 보안이 중요한 Secure Enclave memory 의 replay 를 막기위해 사용되어진다.

Secure Enclave 에 의해 file system 에 저장된 data 는 UID 와 entangled 된 key와 anti-replay counter 로 암호화되어 진다. anti-replay counter 는 dedicated 비휘발성 메모리 IC 안에 저장되어 있다.

A12 와 S4 SoC 들이 있는 기기들에서, Secure Enclave 는 anti-replay counter storage 를 위해 secure storage IC 와 짝을 이룬다. secure storage IC 는 immutable ROM code, 하드웨어 random number generator, 암호화엔진들, physical tamper detection(물리적인 조작 감지) 과 함께 디자인됐다. counter 들을 읽고 업데이트 하기 위해서, Secure Enclave 와 storage IC 는 counter 들에게 exclusive access 를 보장하는 '안전한 protocol '를 사용한다.

Secure Enclave 의 "anti-replay 서비스"들은 event 에 사용된 data 의 철회(revocation of data over events)를 위해 사용되어진다.

이 이벤트들은 anti-replay 의 경계들(boundaries)을 표시 해 준다.
이벤트는 다음것들을 포함한다. 하지만 제한되진 않는다.
  • Passcode change
  • Touch ID or Face ID enable/disable
  • Fingerprint add/delete
  • Face ID reset
  • Apple Pay card add/remove
  • Erase All Content and Settings
Secure Enclave 는 또한 지문을 처리하고, Touch ID 와 Face ID 센서들로 부터 오는 얼굴데이터를 처리하는 책임도 진다. 그리고 맞는지 여부를 결정하고, 그리고나서, 유저를 대신해서 access 또는 구매를 가능하게 한다.

Reference

  1. https://www.apple.com/business/site/docs/iOS_Security_Guide.pdf

[컴][웹] AdoniJS at Start up

아도니스 제이에스 / js


Adonisjs at startup

app.js

_loadPreLoadFiles 이전에 this._registerProviders()와 await this._bootProviders()를 호출한다. 이 때 app.js 에 접근해서 Providers 를 가져오고, 이 Provider 의 register() 와 boot() 을 호출한다.

preloaded files

아도니스js 에서 처음에 load 하는 file 들은 다음과 같다.(_loadPreLoadFiles)
  • 'start/routes',
  • 'start/events',
  • 'start/socket',
  • 'start/kernel',
  • 'start/wsKernel'
이 부분은 Ignitor source 에서 확인할 수 있다.
new Ignitor(require('@adonisjs/fold'))
  .appRoot(__dirname)
  .fireHttpServer()
  .catch(console.error)



class Ignitor {
  constructor (fold) {
    this._fold = fold
    this._appRoot = null
    this._modulesRoot = null
    this._loadCommands = false

    /**
     * Files to be preloaded
     *
     * @type {Array}
     */
    this._preLoadFiles = [
      'start/routes',
      'start/events',
      'start/socket',
      'start/kernel',
      'start/wsKernel'
    ]

    /**
     * Default app file
     *
     * @type {String}
     */
    this._appFile = 'start/app.js'
    ...
  }
  ...
  async fire () {
   ...
   this._registerProviders()
   await this._bootProviders()
   ...
   this._loadPreLoadFiles()
  }

  async fireHttpServer (httpServerCallback) {
    try {
      await this.fire()
      await this._startHttpServer(httpServerCallback)
    } catch (error) {
      this._printError(error)
    }
  }

  /**
   * Return the exported values from the appFile. Also
   * it will validate the exports object to have all
   * required keys.
   *
   * @method _getAppAttributes
   *
   * @return {Object}
   *
   * @private
   */
  _getAppAttributes () {
    return require(path.join(this._appRoot, this._appFile))
  }

  /**
   * Registers an array of providers to the Ioc container. This
   * method will make use of the `appFile` to get the providers
   * list.
   *
   * @method _registerProviders
   *
   * @return {void}
   *
   * @private
   */
  _registerProviders () {
    this._callHooks('before', 'providersRegistered')

    /**
     * Getting list of providers and registering them.
     */
    const { providers, aceProviders } = this._getAppAttributes()
    const providersToRegister = this._loadCommands ? providers.concat(aceProviders) : providers
    this._fold.registrar.providers(providersToRegister).register()

    debug('registered providers')
    this._callHooks('after', 'providersRegistered')
  }

  /**
   * Boot providers
   *
   * @method _bootProviders
   *
   * @return {void}
   *
   * @async
   *
   * @private
   */
  async _bootProviders () {
    this._callHooks('before', 'providersBooted')

    /**
     * The providers set set on `registrar` when they were registered. We
     * use the same set to boot the previously registered providers.
     */
    await this._fold.registrar.boot()

    debug('booted providers')
    this._callHooks('after', 'providersBooted')
  }

  ...
  _loadPreLoadFiles () {
    this._callHooks('before', 'preloading')
    ...

    this._preLoadFiles.forEach((file) => {
      const filePath = path.isAbsolute(file) ? file : path.join(this._appRoot, file)

      /**
       * Require file when it's not optional or when optional
       * file exists
       */
      if (!this._isOptional(file) || this._fileExists(filePath)) {
        require(filePath)
      }
    })

    this._callHooks('after', 'preloading')
  }
}



[컴][웹] 간단하게 주기적으로 message 를 수신하는 web page

EventSource / cors / jsonp / different host / subscribe model /


tornado

EventSource object | Javascript


EventSource object 로 server 에 연결(subscribe) 을 해서 주기적으로 event 를 받을 수 있다. 자세한 동작은 일단 생략한다. ref. 2 에서 어느정도 설명을 해준다.


different domain

domain 이 다른 곳에 event 를 날려주는 server 가 있어도 무리없이 사용할 수 있다. 보통 html(javascript) 에서 다른 domain 인 경우는 보안을 이유로 ajax 요청이 되지 않는다. 그래서 jsonp 등(일반적으로 Cross-Origin Resource Sharing 이라고 이야기하는 방법)을 이용한다.

하지만 이 EventSource 는 그런 것의 제약을 받지 않는다. [ref. 1]

아래처럼 다른 domain 인 경우에는 다른 domain 을 uri 로 적어주면 된다.

var evtSource = new EventSource("//api.example.com/ssedemo.php", { withCredentials: true } );

evtSource.addEventListener("ping", function(e) {
  var newElement = document.createElement("li");
 
  var obj = JSON.parse(e.data);
  newElement.innerHTML = "ping at " + obj.time;
  eventList.appendChild(newElement);
}, false);


이렇게 다른 domain 에 대한 제약이 없어서 좋은 점은 기존의 web server 와 관계없이 새로운 event 처리를 하는 server 를 사용할 수 있다는 점이다. 그러면 주기적인 push event 등을 처리하는 server 를 따로 둘 수 있다.



Server side event vs WebSocket

event driven page 를 만들 땐 SSE 를 사용해도 되고, WebSocket 을 사용해도 된다. 개인적으로는 필요한 부분은 단방향이라서, 양방향 통신이 되는 WebSocket 이 굳이 필요하지 않았다.

아래는 SSE 와 WebSocket 을 간략하게 정리했다.

SSE

  1. 2009년 4월 23 일 WHATWG 에서 승인했다. [ref. 7] 
  2. 하지만 SSE 가 좀 더 간편하다. 
  3. 그리고 http protocol 위에서 구현되었다. 
  4. 단방향 통신
  5. client 의 connection 이 종료되었는지 여부를 알 수는 없다.(http 라서 당연한 것일지도.)


WebSocket

  1. 반면에 WebSocket 은 Tcp/ip 로 다른 port 를 사용한다. 그래서 이 port 가 firewall 에 의해 막혀 있을 수도 있고, 
  2. 이 녀석을 이용하려면, protocol 을 또 정의해야 한다. (ref. 5 에서 좀 더 다양한 의견을 확인할 수 있다.)
  3. 양방향 통신






Reference

  1. Using server-sent events - Web APIs | MDN
  2. Stream Updates with Server-Sent Events - HTML5 Rocks
  3. Asynchronous connections - RethinkDB
  4. Build a real time data push engine using Python and Rethinkdb | IMPYTHONIST
  5. SSE vs Websockets - Streamdata.io
  6. Lessons Learned Architecting Realtime Applications | Lincoln Loop
  7. Python and Real-time Web | Eat at Joe's
  8. Tornado server-sent events · GitHub : tornado 로 SSE 구현 예제.
  9. Building RESTful APIs with Tornado | Dr Dobb's

[컴][웹] V8 에서 JavaScript 의 pipeline

v8 engine 의 동작 / 동작원리 / 크롬 자바스크립트 엔진 / 크롬 엔진 / 크롬 렌더링 엔진 /



V8 에서 JavaScript 의 pipeline

v5.9 이전의 pipeline

from: https://v8.dev/blog/ignition-interpreter

원래는 baseline compiler(위의 그림에서 Ignition 과 Full codegen 의 위치에 있는 것이라 여기면 될 듯 하다.) 가 machine code 를 빠르게 만들고, 이 code 가 실행되는 동안에 이 code를 분석하고 일부를 optimizing compiler 가 optimized code 로 다시 compile 한다.

이 때 사용되는 2개의 optimizing compiler 가 crankshaft, turbofan 이다.

  • TurboFan : Ignition의 bytecode를 바로 최적화된 machine code 로 바꿀 수 있다.[ref. 4]
  • Crankshaft: 소스코드를 다시 컴파일을 해서 최적화된 machine code 를 만든다.[ref. 4]
참고로, v5.9 부터 Full-codegen 과 Crankshaft 는 사용하지 않는다.[ref. 4]

Ignition 의 등장

그런데 이 상황에서 Ignition 을 만들어서 "baseline compiler 가 machine code 를 만드는 것"을 대신해서 Bytecode 를 만들게 했다.

Ignition 는 처음에 모바일에서 사용하기 위해서 만들었다.[ref. 4]  JIT 가 만든 machine code 의 size 가 커서 메모리를 너무 잡아먹었기 때문이다.  Ignition 에 의해서 chrome tab 마다 메모리를 약 5% 정도 아꼈다.

Ignition 은 bytecode 를 생성하는 compiler 이고, 이녀석은 이전의 baseline compiler 를 대체한다.  Ignition 이 bytecode 를 만들때도 당연히 최적화를 한다.

이 Ignition 은 register machine 이다. 이것은 stack machine 과 다르다. 이건 내 생각이지만, 안드로이드의 경험이 덕을 본듯 하다. (stack-based vs register-based)

Ignition 이 생기면서 고성능의 interpreter 가 생겼고 이 녀석이 Ignition 이 만든 bytecode 를 실행해주는데, 실제웹사이트에서 속도가 이전의 baseline compiler 가  만든 code 의 속도에 근접한다.

References

  1. Ignition · V8 : ignition 에 대한 여러자세한 설명들의 link 들이 있다.
  2. Firing up the Ignition interpreter · V8
  3. Home · v8/v8 Wiki · GitHub
  4. Launching Ignition and TurboFan · V8
  5. TurboFan · V8 : turbofan 에 대한 정보들이 모여 있다.


[웹][컴] Digging into the TurboFan JIT 번역




TurboFan JIT

TurboFan JIT 은 이름에서 알 수 있듯이 Just In Time interpreter(runtime, vm 뭐라고 부르든 상관없을듯) 라고 보면 될 듯 하다.

ref. 1 은 TurboFan 의 design 에 대한 이야기이다. 여기서는 ref. 1 의 이야기를 정리하는 수준으로 작성할 것이다. 자세한 이야기는 ref. 1을 확인하자.

이전에 쓰던 JIT 이 CrankShft JIT 이다.
  • CrankShaft JIT --> TurboFan JIT

TurboFan 의 장점-layered architecture

  • 아래 3개를 좀 더 명확하게 분리 시켰다. --> 좀 더 명확하고, 견고한 code 를 가능하게 한다.
    • source-level language (JavaScript)
    • the VM's capabilities (V8)
    • the architecture's intricacies (from x86 to ARM to MIPS)
  • 최적화기능들(optimizations)과 기능(feature)들을 구현시 효과적으로 할 수 있다.
    • code 가 architecture-dependent backend 들과 분리돼서, 새롭게 추가되는 Javascript 의 기능들을 추가하기 수월해 졌다.
  • 좀 더 효과적인 unit test 를 작성할 수 있다.
  • code 도 줄여준다.(CrankShaft 에서 13,000~16,000 라인이던 부분이 3,000 라인 미만)
  • 여러 architecture 의 engineer 가 좀 더 효율적으로 작업할 수 있게 해준다.

좀 더 수준높은 최적화 방법들

TurboFan JIT 은 이전의 CrankShaft 보다 좀 더 발전된 기술을 이용해서 좀 더 공격적인 optimization 들을 한다.
  • Sea of Nodes IR: 디자인의 핵심은 코드의 "좀 더 유연해진 Sea of Nodes IR(internal representation)" 이다. 이것이 좀 더 효과적인 "reordering" 과 "최적화(optimization)" 을 가능하게 해준다.
  • TurboFan 은 범위 분석(Numerical range analysis) 을 통해 number-crunching code 를 이해하는 것을 돕는다.
  • The graph-based IR : graph 의 기반한 IR 은 대부분의 최적화기능들이 "simple local reductions" 들로 표현되는 것을 가능하게 해준다. simple local reductions 은 독립적으로 작성하고 테스트하기 좀 더 쉽다. 최적화 엔진은 이 local rules 들을 체계적이고, 빈틈없이 적용한다.
  • graphical representation  을 벗어나는 것은  코드를 loop 에서 덜 자주 사용되는 path들로 옮기기 위해 "혁신적은 scheduling 알고리즘"을 사용한다.
  • 이 알고리즘은 reordering freedom 을 이용한다.
  • architecture-specific optimizations: 최종적으로, 복잡한 "instruction selection" 같은 architecture-specific 최적화 기능들은 좋은 품질의 code 를 위해 각 target platform 의 기능들(features)을 이용한다.

See Also

[컴][웹] V8 컴파일 하기

v8 engine compilation / v8 chrome engine / 브라우저 엔진 컴파일 / javascript engine / js engine v8


v8 compilation

컴퓨터환경

  • CPU : i3-2100 CPU@3.10GHz
  • RAM : 8GB
  • DISK : SATA3(6Gb/s), 7200RPM, 16MB

windows 에서 빌드

절차

  1. python 설치
  2. python 경로를 PATH 환경변수에 추가
  3. Visual Studio 설치(약 40분)
  4. Windows 10 SDK 설치
  5. depot_tools 설치
  6. path 설정
  7. gclient
  8. fetch v8(약 10분)
    1. 만약 update 한다면, gclient sync
  9. gn gen --ide=vs out/MyV8

python 2.7.15

python 을 설치하고 환경변수 PATH 에 path 를 추가하자.

pip install pypiwin32

win32 module 을 third-party toolchain 에서 사용한다. 그래서 필요하다.

Visual Studio 설치

windows 에서 v8 를 build 하려면 compiler 가 설치되어 있어야 한다. 여기서는 Visual Studio 2017 Community version 을 사용할 것이다.
  • 버전: Visual Studio 2017 (>=15.7.2) 또는 2019 (>=16.0.0) 가 필요[ref. 1]
"C++를 사용한 데스크톱 개발" 을 선택하고, 설치 세부정보 에서 "x86 및 x64용 Visual C++ MFC" 를 추가한다. 아래 command line 으로 바로 설치할 수 있다.
vs_community.exe --add Microsoft.VisualStudio.Workload.NativeDesktop --add Microsoft.VisualStudio.Component.VC.ATLMFC --includeRecommended

Windows 10 SDK 설치 - Debugging Tools

version 10.0.17134 이상의 Windows 10 SDK 가 설치되어 있어야 한다.
아래에서 setup 을 download 하고, "Debugging Tools for Windows" 를 선택하자.
그러면 아래 경로에서 .msi 를 찾을 수 있다. 이녀석을 실행해서 Debugging Tools 를 설치하자.
  • 64bit용 : <Windows Kits>\10\WindowsSDK\Installers\X86 Debuggers And Tools-x64_en-us.msi

depot_tools

depot_tools 를 설치하자. 참고로 python 은 이미 설치되어 있었다.
  1. depot_tools.zip 를 다운받아서 압축을 풀자. 여기서는 c:\v8\depot_tools\ 에 풀었다.
  2. 이 경로를 PATH 환경변수에 추가하자.
  3. DEPOT_TOOLS_WIN_TOOLCHAIN 변수를 설정하고, 0으로 set 하자. 0 으로 set 하면 이미설치된 Visual Studio 를 사용한다는 뜻이다.
  4. gclient 를 실행하자. metrics.cfg 를 만들어준다.
    c:\v8\depot_tools\>gclient

windows defender 에 '제외'(exclusion) 추가

빌드 속도의 향상을 위해 v8 src directory 를 '제외' 항목에 추가하자. (참고)

fetch v8

이 명령어로 checkout 을 하게 된다. 처음에 git source 를 받을 때 사용한다.
> cd c:\v8\v8_chromium
> fetch v8

참고로, fetch chromium 은 desktop chromium 의 소스를 가져온다.  잘 못 알아서, fetch chromium 을 했는데, master 소스를 가져오는데에만 2시간 55분 정도 소요됐다. 그 후에 다른 관련 소스를 또 받아오는데 25분정도가 더 소요됐다.

HDD는 최소 25GB 는 사용된 듯 하다. ref. 1 을 보면 build 에 최소 100GB 가 필요하다고 한다.

v8 은 그래서 아래 경로에 fetch 가 됐다. v8 source code 의 크기만 1.8GB 정도가 된다.
  • c:\v8_chromium\src\v8
그 다음 nacl(Google Native Client) tools 를 download 한다.

마지막으로 아래처럼 username 을 물어본다. 그냥 enter 를 치면, build 를 시도하다 실패한다.

Username for 'https://chrome-internal.googlesource.com':

________ running 'C:\Python27\python.exe v8/build/vs_toolchain.py update' in 'c:\v8\v8_engine'



No downloadable toolchain found. In order to use your locally installed version of Visual Studio to build Chrome please set DEPOT_TOOLS_WIN_TOOLCHAIN=0.
For details search for DEPOT_TOOLS_WIN_TOOLCHAIN in the instructions at https://chromium.googlesource.com/chromium/src/+/master/docs/windows_build_instructions.md


Traceback (most recent call last):
  File "v8/build/vs_toolchain.py", line 502, in <module>
    sys.exit(main())
  File "v8/build/vs_toolchain.py", line 498, in main
    return commands[sys.argv[1]](*sys.argv[2:])
  File "v8/build/vs_toolchain.py", line 444, in Update
    subprocess.check_call(get_toolchain_args)
  File "C:\Python27\lib\subprocess.py", line 190, in check_call
    raise CalledProcessError(retcode, cmd)
subprocess.CalledProcessError: Command '['C:\\Python27\\python.exe', 'c:\\v8\\v8_engine\\v8\\third_party\\depot_tools\\win_toolchain\\get_toolchain_if_necessary.py', '--output-json', 'c:\\v8\\v8_engine\\v8\\build\\win_toolchain.json', '3bc0ec615cf20ee342f3bc29bc991b5ad66d8d2c']' returned non-zero exit status 1
Error: Command 'C:\\Python27\\python.exe v8/build/vs_toolchain.py update' returned non-zero exit status 1 in c:\v8\v8_engine
Hook ''C:\Python27\python.exe' v8/build/vs_toolchain.py update' took 601.97 secs
Traceback (most recent call last):
  File "c:\v8\depot_tools\\fetch.py", line 306, in <module>
    sys.exit(main())
  File "c:\v8\depot_tools\\fetch.py", line 301, in main
    return run(options, spec, root)
  File "c:\v8\depot_tools\\fetch.py", line 295, in run
    return checkout.init()
  File "c:\v8\depot_tools\\fetch.py", line 137, in init
    self.run_gclient(*sync_cmd)
  File "c:\v8\depot_tools\\fetch.py", line 82, in run_gclient
    return self.run(cmd_prefix + cmd, **kwargs)
  File "c:\v8\depot_tools\\fetch.py", line 71, in run
    subprocess.check_call(cmd, **kwargs)
  File "C:\Python27\lib\subprocess.py", line 190, in check_call
    raise CalledProcessError(retcode, cmd)
subprocess.CalledProcessError: Command '('C:\\Python27\\python.exe', 'c:\\v8\\depot_tools\\gclient.py', 'sync', '--with_branch_heads')' returned non-zero exit status 2

c:\v8\v8_engine>

gclient sync

추후에 source 를 최신으로 update 할 때 사용하면 된다.
c:\v8\v8_engine\v8>gclient sync
Syncing projects: 100% (24/24), done.
Running hooks:  75% (21/28) clang
________ running 'C:\Python27\python.exe v8/tools/clang/scripts/update.py' in 'c:\v8\v8_engine'
Downloading https://commondatastorage.googleapis.com/chromium-browser-clang/Win/clang-349417-2.tgz .......... Done.
Copying C:\Program Files (x86)/Microsoft Visual Studio/2017/Community\DIA SDK\bin\amd64\msdia140.dll to c:\v8\v8_engine\v8\third_party\llvm-build\Release+Asserts\bin
Hook ''C:\Python27\python.exe' v8/tools/clang/scripts/update.py' took 19.46 secs
Running hooks: 100% (28/28), done.

c:\v8\v8_engine\v8>

gn gen --ide=vs out/MyV8

chromium 은 ninja 를 사용한다. depot_tools 를 설치하면 같이 설치된다. gn 을 실행하면 .ninja 를 만들어준다. (자세한 내용은 여기 를 참고하자.)

여기서는 Visual Studio 에서 build 를 할 것이라서 --ide=vs 를 추가해 줬다.
c:\v8\v8_engine\v8>gn gen --ide=vs out\MyV8
Generating Visual Studio projects took 277ms
Done. Made 132 targets from 76 files in 9360ms

c:\v8\v8_engine\v8>

이제 아래 경로에 .sln file 이 생성됐다.
  • c:\v8\v8_engine\v8\out\MyV8\all.sln
몇몇 관련 project 를 filetering 하려면 아래 처럼 하면 된다. 하지만 이 option 은 chromium 같이 큰 프로젝트를 열때나 필요할 듯 하다. v8 project 만 열때는 필요치 않았다.
gn gen --ide=vs --filters=//third_party/v8/* --no-deps out\Default

open .sln(약 60분)

이제 위에서 생성된 all.sln 파일을 더블클릭해서 Visual Studio 를 열자. 그리고 build 를 하면 된다. build 를 하면 내부적으로 ninja 를 호출해서 build 를 하게 된다.

여기서는 v8_hello_world 를 build 했다. 대략 60분 정도가 소요됐다.

error case : visual studio 2015 --> visual studio 2017

v8 build 를 위해서 visual studio 의 version 을 upgrade 한 경우는 stdio.h 등의 header files 가 include 가 안되는 경우가 있다. 이때는 Windows SDK 버전을 재설정 해주자.

v8_hello_world.exe 실행

이제 아래 path 에 v8_hello_world.exe 가 생성됐다. 이 녀석을 실행하면 아래처럼 결과가 나온다.
c:\a\programming\v8\v8_engine\v8\out\MyV8>v8_hello_world.exe
Hello, World!
3 + 4 = 7

debug mode

당영한 이야기지만, 만약 이 v8_hello_world 를 디버그 모드로 실행하고 싶다면, solution explorer 에서 v8_hello_world project 에서 context menu(마우스 오른쪽 버튼) 을 눌러서 debug 를 해야 한다.

See Also


  1.  V8 에서 JavaScript 의 pipeline

References

  1. Checking out and Building Chromium for Windows
  2. v8/v8.git - Git at Google

[컴][OS] ELF file format

리눅스 파일포맷 / 실행 파일 포맷 / 규격 / 파일 규격

ELF format

ELF file format 에 관한 얘기를 해보자.

elf header 관련 정보들

일단 여기서 이야기하는 것은 아주 첫 부분이다. 자세한 정보들은 아래 링크를 참고하자.

ELF type

ELF file 은 여러가지 모양(type)이 가능한데,
그 중에 주요한 3가지 type 이 있다.
  1. relocatable file
  2. executable file
  3. shared object file

일단 다른것은 생각하지 말고, 흔히 windows 에서 얘기하는 .exe 같은 executable file 에 대해서 살펴보자.

ELF executable

ELF file 안에는 아래같은 요소가 들어간다.
  1. ELF header : 언제나 ELF file 의 가장 처음에 위치
  2. Program header table
  3. Segment
  4. Section
  5. Section header table.

ELF header 는 항상 처음에 위치하지만, 하지만 다른 것들의 위치는 고정되어 있지 않다.

<그림필요>

ELF header

먼저 이 ELF header 에 대해 얘기 해보자.

역시 위치가 고정되어 있기 때문에 elf file 를 까봐도 찾기가 가장 편할 것이다. 일단 코드상으로는 아래와 같다.

code

e_ident[] Identification indexes
Name Value Purpose
EI_MAG0 0 File identification
EI_MAG1 1 File identification
EI_MAG2 2 File identification
EI_MAG3 3 File identification
EI_CLASS 4 File class
EI_DATA 5 Data encoding
EI_VERSION 6 File Version
EI_PAD 7 Start of padding bytes
EI_NIDENT 16 e_ident[]

struct
#define EI_NIDENT    16
typedef struct{
    unsigned char    e_ident[EI_NIDENT];      /*Magic number and other info */
    ElfN_Half       e_type;                         /*Object file type */
    ElfN_Half       e_machine;                   /* Architecture */
    ElfN_Word       e_version;                     /* Object file version */
    ElfN_Addr       e_entry;                       /* Entry point virtual address */
    ElfN_Off        e_phoff;                       /* Program header table file offset */
    ElfN_Off        e_shoff;                        /* Section headr table file offset */
    ElfN_Word       e_flags;                        /* Processor-specific flags */
    ElfN_Half       e_ehsize;                      /* ELF header size in bytes */
    ElfN_Half       e_phentsize;                 /* Program header table entry size */
    ElfN_Half       e_phnum;                    /* Program header entry count */
    ElfN_Half       e_shentsize;                  /* Section header table entry size */
    ElfN_Half       e_shnum;                      /* Sectino header table entry count */
    ElfN_Half       shstrndx;                      /* Sectino header string table index */
} Elf32_Ehdr


binaries

실제로는 어떻게 이 값이 hex 로 저장되어 있는지 살펴보자.

<그림>

확대


당연한 이야기지만 이 구조체 structure 를 보면 header에 어떤 것들이 들어있는지 알 수 있다.


아래와 같은 elf file 이 있다고 하자.
0x7f, 0x45, 0x4c, 0x46, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x28, 0x00, ...
e_ident
처음 16개의 byte 들(e_ident[EI_NIENT])을 살펴보자.

  • EI_MAG*
    • 0x7f, 0x45, 0x4c, 0x46
    • 처음 4개는 이 파일이 ELF object file 인 것을 알려주는 magic number 이다.
  • EI_CLASS
    • 그 다음 byte, 즉 5번째 byte 는 file 의 class 를 나타낸다.
    • ELF file 이 다양한 사이즈의 머신에서 가져다 쓸 수 있게 하기 위해서 이 ELF file 이 어떤 machine 에서 작동하는 놈이다라는 것을 알려줄 필요가 있다.
      • '1'은 32-bit objects 라는 것(ELFCLASS32)을 알려주며,
      • '2'는 64-bit objects 라는 것(ELFCLASS64)을 알려준다.
  • EI_DATA
    • 6번째 BYTE는 encoding type 을 알려준다.
      • '1'(ELFDATA2LSB) : little-endian
      • '2'(ELFDATA2MSB) : big-endian
  • EI_VERSION
    • 7번째 byte 는 ELF header version 을 알려준다.
    • 보통 '1'(EV_CURRENT) 를 사용한다.
  • EI_PAD
    • 8번째 byte 부터  끝까지 '0'으로 채워져 있는데, 그냥 의미없이 채워넣은 것이다, 즉 padding 이다. 흔히 하는 말로 reserved, 예약되어 있는 것이다.
    • (EI_PAD 이 값이 현재 '7'인데, 만약 header에 들어갈 값이 더 생긴다면 당연히 EI_PAD 값을 큰 수로 변경해야 한다.)
e_type
17번째 byte 와 18번째 byte 는 지금 ELF file 의 format 을 알려준다.
자세한 사항은 놔두고, 일단 여기서는 executable file 이기 때문에 '2' 가 된다.


[컴][웹] Launching Ignition and TurboFan 번역


Launching Ignition and TurboFan

Launching Ignition and TurboFan 의 내용을 대략적으로 번역했다.

JavaScript execution pipeline

새로운 자바스크립트 수행 파이프라인
  • - Ignition : V8’s interpreter
  • - TurboFan : compiler
v5.9 부터 2010년부터 사용해온 Full-codegen 와 Crankshaft 를 더이상 사용하지 않가로 함.

TurboFan

TurboFan 은 처음 디자인할 때부터 ES2015 이후의 기능(feature) 에 대한 최적화도 고려했다.

터보팬은 layered compiler design 을 도입했다.
high-level 과 low-level compiler optimizations 을 분리해서 새로운 언어기능(language features) 을 추가하기 쉽게

명확한 명령어 선택 컴파일 phase(explicit instruction selection compilation phase)를 추가해서
구조와 덜 연관된 코드를 만들 수 있게 해준다.

Ignition

Ignition interpreter 의 최초의도는 모바일기기에서의 메모리 사용을 줄이기 위함이었다.

이전에 Full-codegen baseline compiler가 생성한 코드는 크롬에서  평균 javascript heap 의 1/3을 차지했다.

그래서 어플리케이션의 실제 데이터가 사용할 수 있는 공간이 적었다.

후에 V8팀은 TurboFan 을 이용해서 Ignition의 bytecode를 바로 최적화된 machine code 로 바꿀 수 있는 사실을 이용했다.

소스코드를 다시 컴파일을 해야만 했던 Crankshaft

Ignition의 bytecode는 v8에서 좀 더 깔끔하고 덜 error-prone 한 기준선(baseline)을 제공한다.
 Full-codegen의 baseline compiled code를 생성하는 것보단 bytecode 를 생성하는 것이 더 빠르다.

그래서 ignition을 사용하게 되면 script startup 시간과 웹페이지 로드 속도가 빨라진다.

See Also

  1. Digging into the TurboFan JIT 번역
  2. Firing up the Ignition interpreter
  3. V8 에서 JavaScript 의 pipeline

References

  1. Launching Ignition and TurboFan

[컴][웹] 정부의 https 접속문제 원인

왜 정부사이트는 https 가 안되나 / 보안접속이 안되는 이유 / 정부사이트의 문제 / 정부사이트가 욕을 먹는 이유/ 정부 사이트에서 https 가 안되는 이유


정부의 https 접속문제 원인


현재 정부 사이트들을 https 로 접속하면 문제가 있다. 이것은 보통 브라우저가 신뢰할 만한 기관이라고 인정하는 녀석들의 리스트를 가지고 있는데, 그 리스트에 한국정부가 들어가지 못해서이다.


처음에 나는 이것이 단순히 인지를 못해서 추가를 못한 수준인줄 알았다. 하지만 기사가 더 자세한 내용을 알려줬다. 기사를 차라리 몰랐으면 할 정도로 정부의 IT 인식 수준(특히 보안)이 참담했다. 여기서는 그 기사의 내용을 좀 더 정리해 보도록 하겠다.



문제점

인증받지 못한 행정안전부가 발급하는 'G-SSL 인증서'

인증서는 누구나 발행할 수 있다. 하지만, 누가발행했느냐를 보고, 브라우저는 이 인증서가 믿을만한 인증서인지 아닌지 여부를 판단한다. 브라우저는 믿을만한 발행 업체에 대한 리스트를 가지고 있다.

즉, 많은 브라우저 업체(구글, 모질라, 애플)들이 한국 정부의 CA (Certificate authority)자격을  인정하지 않는다는 뜻이다.

하지만 그래도 되는 브라우저들이 있다. 이것은 브라우저가 자신의 CA 리스트를 사용하는 대신 OS가 가지고 있는 CA list 를 사용하기 때문이다.


모질라의 CA 인증서 프로그램에서 탈락

2015년 11월


  • 2015년말부터 행정안전부(당시 '행정자치부') 가 CA 자격을 얻기 위해 노력.
  • --> 그 일환으로 모질라의 CA인증서프로그램(CA Certificate Root Program, 이하 '프로그램')을 신청(관련자료)
  • --> 이 검증이 대체로 아무문제가 없다면 2년이 소요된다고 한다.

모질라의 검증


  • 검증 작업: 2016년 초부터 2018년 초까지 약 2년간 진행
  • 행정안전부 산하기관인 "한국지역정보개발원(KLID)"의 담당자가 실무를 맡음
  • KLID가 실질적인 G-SSL 인증서 발급 및 관리체계 운영 조직

모질라의 최종판단


  • 한국 정부의 G-SSL 인증서 발급 및 관리 체계가 프로그램 검증을 위해 내걸고 있는 '루트저장소정책(Root Store Policy)' 등 준칙의 기준에 맞지 않는다고 판단


왜 탈락했는가?

ref. 1 에 따르면, 모질라의 최종판단은 아래 3개의 사건에 근거한다고 한다. 아래는 대략적인 정리이다. 조금 더 자세한 사건 내용은 ref. 1 을 참고하자.

  • 사건1: 2016년 2월 모든 GPKI 인증기관 인증서 검증 신청 기각
    • 처음에는 행정자치부를 Root CA 로 해서 행정자치부만 검증을 받으려 함.
    • 행정자치부는 GPKI 기반의 인증서 / 교육부는 EPKI라는 인증체계 기반의 인증서를 발급
    • 모질라는 수퍼 CA 하나만이 아니라 실질적인 Root CA 역할을 하는 모든 기관이 개별적으로 검증을 받던지, GPKI 를 재설계 해야 한다고 지적.
    • 결국 행자부와 교육부는 개별적으로 인증서 검증을 받기로 함.
    • 또한 행자부 밑에 sub CA 로 있던  대법원, 대검찰청, 병무청도 빠짐. 이들은 원래 검증기관에 CP 및 CPS 문서를 제출하지 않고, 외부기관 감사도 받지 않는 것을 전제로 sub CA 에 들어있었는데, 모질라가 인증서를 발급하는 주체는 모두 검증대상이라고 했기 때문이다.
  • 사건2: 2018년 3월 행정안전부·교육부 인증서 부실관리 실태 노출
    • 2018년 4월초 행정안전부와 교육부, 두 정부부처의 인증서 발급이 이상했다.
      • 구글 포럼 제보
      • 1. 행정안전부 행정전자서명인증센터 사이트 인증서 :  유효한 '온라인인증서상태프로토콜(OCSP)' response을 제공하지 않는다
      • 2. 교육부 행정전자서명인증센터 사이트 인증서 : HTTP 인증서해지목록(CRL) endpoint 및 OCSP HTTP endpoint 정보가 없다.
    • 모질라 측은 4월 중순 한국 정부에 해명을 요구
    • 결국 8월 검증 절차를 중단
  • 사건3: 2018년 4월 인증서 부정발급 실태 노출
    • 2018년 4월초 모질라의 온라인 포럼인 '버그질라'에는 또다른 두 가지 인증서 관련 문제가 추가로 제보(1451235 - Distrust the WebTrust Audit of Deloitte Anjin South Korea)
      • 1. 행정안전부와 교육부가 도메인검증을 하지 않은 인증서를 발급
      • 2. 교육부가 교육기관용으로 각 기관별 도메인이 아닌 최상위도메인(TLD) 범주를 지정한 '와일드카드' 인증서를 발급. 
        • 교육부가 웹트러스트 인증을 받은 시점에 발생
        • 교육부는 웹트러스트 인증을 위해 '딜로이트안진'에게 외부감사를 받았었다. 교육부가 외부감사를 통과했는데, 문제가 발생해서 '딜로이트안진'의 감사 신뢰성이 무너졌다.
        • 행정안전부의 감사도 '딜로이트안진' 이 맡았었기에, 행정안전부의 감사결과도 신뢰를 잃게 되었다.
    • 당시 문제가 된 인증서들은 곧 폐기



사건3 이 문제가 되는 이유는 우리가 https 를 사용하기 위해 인증서를 신청하고, 발급받는 과정을 생각해 보면 이해가 간다.




References


  1. 정부, 공공사이트 HTTPS '보안경고' 못 없앴다 - 지디넷코리아




[컴][알고리즘] AES 암호화 설명






  •  key expansion 은  key 를 4-byte 씩 쪼개어서 array 에 담는것
  • 16-byte 씩 작업을 하게 된다. 4 x 4 의 matrix 이다. 이 하나의 matrix을 state 라 부른다. 아래그림 참조
  • 각 정해진 bit 마다 round 가 정해져 있다.(128bit 은 10round, 256bit 는 14 round)
    • 각 bit 의 암호마다 "key-length/block-size/rounds" 수가 정해져 있다.
  • 이 (round-1)수 만큼 loop 을 돈다. 그리고 마지막 round 만 따로 한다.
  • 마지막 round 는 mix-columns 작업을 하지 않는다.

AES encryption process

1. SubBytes

sub 가 substitute 라고 보면 된다. State 에 있는 각 위치의 byte 에 해당하는 값을 "특정 table" 의 값과 교환하게 된다. 이 table 은 아래처럼 정해져있다.

여기 를 보면 어떻게 SubBytes 를 하는지 알 수 있다.

S = [ 
0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5, 0x30, 0x01,
0x67, 0x2b, 0xfe, 0xd7, 0xab, 0x76, 0xca, 0x82, 0xc9, 0x7d,
0xfa, 0x59, 0x47, 0xf0, 0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4,
0x72, 0xc0, 0xb7, 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7, 0xcc,
0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15, 0x04, 0xc7,
0x23, 0xc3, 0x18, 0x96, 0x05, 0x9a, 0x07, 0x12, 0x80, 0xe2,
0xeb, 0x27, 0xb2, 0x75, 0x09, 0x83, 0x2c, 0x1a, 0x1b, 0x6e,
0x5a, 0xa0, 0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, 0x2f, 0x84,
0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b, 0x6a, 0xcb,
0xbe, 0x39, 0x4a, 0x4c, 0x58, 0xcf, 0xd0, 0xef, 0xaa, 0xfb,
0x43, 0x4d, 0x33, 0x85, 0x45, 0xf9, 0x02, 0x7f, 0x50, 0x3c,
0x9f, 0xa8, 0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5,
0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2, 0xcd, 0x0c,
0x13, 0xec, 0x5f, 0x97, 0x44, 0x17, 0xc4, 0xa7, 0x7e, 0x3d,
0x64, 0x5d, 0x19, 0x73, 0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a,
0x90, 0x88, 0x46, 0xee, 0xb8, 0x14, 0xde, 0x5e, 0x0b, 0xdb,
0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c, 0xc2, 0xd3,
0xac, 0x62, 0x91, 0x95, 0xe4, 0x79, 0xe7, 0xc8, 0x37, 0x6d,
0x8d, 0xd5, 0x4e, 0xa9, 0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a,
0xae, 0x08, 0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6, 0xb4, 0xc6,
0xe8, 0xdd, 0x74, 0x1f, 0x4b, 0xbd, 0x8b, 0x8a, 0x70, 0x3e,
0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e, 0x61, 0x35, 0x57, 0xb9,
0x86, 0xc1, 0x1d, 0x9e, 0xe1, 0xf8, 0x98, 0x11, 0x69, 0xd9,
0x8e, 0x94, 0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf,
0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68, 0x41, 0x99,
0x2d, 0x0f, 0xb0, 0x54, 0xbb, 0x16 ]
from: https://www.youtube.com/watch?v=gP4PqVGudtg

2. Shift Rows

아래처럼 각 row 를 shift 한다. 0번째 열은 '0'번, 1번째 열은 '1'번 ...
from: https://www.youtube.com/watch?v=2y_tidbY-Lw

3. MixColumns

여기 를 보면 animation 을 볼 수 있다.

state 의 각 column 을 Galois field 와 곱하기(matrix 곱하기) 를 하게 된다. 만약 GF(255) 라면 0~255 사이의 숫자로만 구성된 field(matrix) 가 만들어진다.



4. Add Round Key

"State 의 각 byte" 와 "Round key 의 각 byte" 을 XOR 한다.
from: https://www.youtube.com/watch?v=2y_tidbY-Lw



CTR mode 사용시 유의할 점




[컴][go] golang 의 interface{}(interface type) 의 itable 이 어떻게 생성될까.

golang itable  / how to generate itable in go / go virtual function table /

Go의 itable 의 생성

interface{}(interface type) 에 대한 itable 이 어떻게 생성되는지에 대한 설명이다.

Go 의 dynamic type conversion 들은 compiler 또는 linker 가 미리 모든 가능한 itable 들을 미리계산하는 것이 합리적이지 않다는 뜻이다.

너무 많은 (interface type, concrete type) pair 들이 존재한다. 그리고 대다수는 필요하지 않을 것이다. 대신에, 컴파일러는 위의 예제의 Binary, int 또는 func(map[int]string) 같은 각 concrete type 에 대해서 type description structure 를 생성한다.

비슷하게, 컴파일러는 interface type 에 대해 다른 type description structure 를 하나 생성한다. 그것 또한 method list 를 갖고 있다. interface runtime 은 interface type 의 method table 에 있는 모든 method를 concrete type의 method table 에서 찾아서 itable 을 계산한다.

runtime 은 itable 을 생성한 후에, 이것을 caching 한다. 그래서 이 것은 한번의 계산만 필요하다.


Computing the Itable

Now we know what the itables look like, but where do they come from? Go's dynamic type conversions mean that it isn't reasonable for the compiler or linker to precompute all possible itables: there are too many (interface type, concrete type) pairs, and most won't be needed. Instead, the compiler generates a type description structure for each concrete type like Binary or int or func(map[int]string). Among other metadata, the type description structure contains a list of the methods implemented by that type. Similarly, the compiler generates a (different) type description structure for each interface type like Stringer; it too contains a method list. The interface runtime computes the itable by looking for each method listed in the interface type's method table in the concrete type's method table. The runtime caches the itable after generating it, so that this correspondence need only be computed once. [ref. 1]

References

  1. research!rsc: Go Data Structures: Interfaces
  2. How to use interfaces in Go by jordan orelli

[컴] cpu가 그래픽처리를 위해 GPU를 사용하는 과정



그래픽카드와 함께 CPU 가 좋아야 하는 이유

내용에 틀린부분이 있을 수 있다. 그러니 의심이 가면 관련 부분을 찾아보기 바란다.

일단 단순하게 표현하면, 던전으로 들어가기 전과 후의 장면은 크게 달라진다. 그런데 캐릭터가 던전으로 들어갔는지, 아니면 그 앞까지 왔는지를 결정하는 것은 graphic 부분이 아닌 다른 code 부분이다. GPU 가 graphic 부분의 일을 처리하겠지만, 그 이외의 logic 을 계산하는 것은 cpu 이다. 그러므로 cpu 가 느리다면, 결국 gpu 가 아무리 빨라도 끊김(흔히 버벅임)을 방지하기는 어렵다.


cpu가 그래픽처리를 위해 GPU를 사용하는 과정

The_CUDA_Handbook.pdf 에서 좋은 설명을 확인할 수 있다. 여기선 일부를 정리해 놓았다.

cpu 와 bus



현재까지, 보드의 south bridge가 대부분의 주변기기들을 담당하고, north bridge 가 memory controller 를 가지고 있다. 그래서 이 north bridge 의 memory controller 를 이용해서 cpu 는 memory 와 통신한다.(이 통신이 이뤄지는 interface 가 front-side bus 이다.)

그리고 north bridge 는 gpu 와 PCI Express interface 를 이용해서 통신한다.(물론 pci express 이전에는 AGP 같은 것이 있었다.)

gpu 안에는 gpu memory controller 가 들어가 있다. 그래서 이 memory controller 를 통해 gpu memory 와 통신한다.

Memory controller 가 CPU 안으로

이러다가 cpu 가 memory controller 를 내장했다.(AMD의 Opteron 과 Intel의 Nehalem (i7) ) 그래서 north bridge 없이 바로 memory 와 통신하게 된다.

그리고 I/O Hub 를 통해 PCI express interface 를 사용해서 GPU 에 접근한다.

HyperTransport (HT) / QuickPath Interconnect (QPI) interface

이 상황에서 cpu 를 여러개 사용하기 위해 cpu 간의 통신이 필요했다.(Multiple CPUs (NUMA)) 그래서 생긴것이 HyperTransport (HT) / QuickPath Interconnect (QPI) 이다. 이 interface 로 cpu core 끼리의 통신과 cpu 와 I/O hub 와의 통신을 하게 된다.

  • cpu core <---> cpu core
  • cpu <---> I/O hub

I/O hub 가 CPU 안으로

인텔의 Sandy Bridge 부터 I/O hub 를 cpu 안에 내장하게 된다. 즉 PCI express 를 내장하게 된 것이다.



CPU/GPU 연동

The_CUDA_Handbook.pdf 의 2.5 부분의 내용이다.

GPU 는 직접적으로 page-lock 된 CPU memory 에 DMA 를 통해 접근할 수 있다.

Page-locking 은 주변 하드웨어가 CPU Memory 에 직접적으로 접근할 수 있도록 해주기 위해 OS가 사용하는 기능이다.

locked page 들은 OS 에 의해 퇴출(eviction) 에 적합하지 않다고 표시된 상태이다. 그래서 device driver들은 이 주변기기들이 메모리에 직접적으로 접근하기 위해 페이지들의 물리적인 주소들을 이용하라고 프로그램할 수 있다.

CPU 는 여전히 불확실한 상태에서 memory 에 접근할 수 있다. 그러나 메모리는 옮겨지거나, disk 로 paged out 되지 않는다.

DMA 는 GPU 가 CPU memory 를 "cpu 의 실행"에 대해 독립적으로 그리고 병력적으로 read/write 할 수 있게 해줬다. 다만 여기선 race condition 들을 피하기 위해 CPU 와 GPU 사이의 싱크를 맞추는 것을 고려해야 한다.
  • Pinned host memory(pinned buffer): GPU 가 직접 접근할 수 있는 CPU memory 부분
  • Command buffers: GPU 실행을 통제하기 위해 CUDA driver 에 의해 write되고, GPU에 의해 read 되는 buffer. 

pinned buffer

pinned buffer 는 direct access 를 위해 GUP 에 의해 mapping 된다. CUDA 에서는 API cudaMallocHost() 를 통해 allocate 할 수 있다.

pinned buffer 가 주로 사용되는 방법중 하나는 GPU 에 command 를 보내는 용도이다. CPU 가 command 를 buffer 에 write 하고, 동시에 이것을 GPU 가 이전에 write된 command 를 read 하고 execute 한다.

이 buffer 의 앞부분(leading edge) 에 CPU 가 command 를 write 한다. 그래서 GPU 가 이때는 이 녀석을 읽을 수 없다.

GPU 는 buffer 의 끝부분(trailing edge) 에서부터 command 를 읽어드려서 실행한다.

이 command buffer 는 circular queue 라고 보면 된다. 그래서 한번 command 처리를 다한 부분은 다시 CPU 가 command 를 write 하기 위해 사용된다.

cpu-bound / gpu-bound

이 상황에서 2가지 case 가 있는데 CPU-bound 와 GPU-bound 다. bound 는 "경계" 의 뜻이니, CPU bound 는 CPU 가 경계가 되는 경우라고 보면 되고, GPU-bound 는 GPU 가 경계가 되는 것으로 보면 된다.
  • 하나는 CPU 의 command 를 write  < GPU 의 처리 속도(CPU-bound)
  • 하나는 CPU 의 command 를 write  > GPU 의 처리 속도(GPU-bound)


References

[컴][웹] Server-Side Event(SSE)

서버 사이드 이벤트 / 폴링 / 푸쉬 메시지 / notification / noti



Server-Side Event(SSE)

여기서는 ref. 2 에 있는 이야기의 일부를 다룬다. 되도록 ref. 2 내용 전체를 보는 것이 더 도움이 될 듯 하다.

대략적으로 이야기하면, 이녀석은 기본적으로 ajax 로 했던 polling 을 browser 가 대신해준다고 생각하면 된다. 즉, polling 을 사용했어야 하는 모든 경우에 사용할 수 있다. 그리고 polling 이 끊겼을때등에 대한 처리에 관한 설정(id 같은, ref. 2를 참고) 도 가능하기 때문에 훨씬 편리하고 좋다.

그리고 ref.2 에 보면 SSE 는 http protocol 로 만들어졌다고 한다. 그래서 이것을 실행하는 주체는 browser 안에서 layer 가 좀 다를 수는 있지만, js script 내에서 polling 을 구현한 것이랑 거의 다를 바가 없을 듯 하다. 여하튼 ref. 2 에서는 이것을 event streaming 이라고 부른다.


event streaming 을 close 하는 법


이렇게 주기적으로 browser 가 server로 부터 event 를 받아오는데, 이것을 close 하는 방법이 2가지 있다.
  • 서버에서 close 하는 법 : 
    • Content-Type 이 text/event-stream 가 아닌 response 를 보내거나
    • 200 이외의 HTTP status 를 보낸다.
  • client 에서 close 하는 법
    • source.close();
다만 이런 방법으로 Event streaming 을 끝내지 않고, 만약 network 가 끊어진 상황, 즉 browser 가 아예통신을 못하거나 한 상황에 의해 event streaming 이 끊긴 경우라면, browser 가 알아서 주기적으로 연결을 위해 시도를 한다.


message event

기본적으로 발생시키는 event 는 message event 이다. 하지만, server 의 응답에 "event" 를 정의해서 browser 로 하여금 새로운 event 를 발생시키도록 할 수 있다.
event: servertime
data: The server time...

source.addEventListener('servertime', function(event) {
    document.getElementById("result").innerHTML += event.data + "<br>";
});



Response

header 조건

  1. Content-Type: text/event-stream
  2. Cache-Control: no-cache

body

  • data: data_you_want_to_put
reponse body 이 딱 data 만 있으면 된다. 다른 option 들도 존재한다.(ref. 2 참고)
data: The server time...




Security 

보안상의 이슈로 client 에서 event handler 를 구현할 때 event 의 origin 을 check 하라고 한다.[ref. 2]
source.addEventListener('message', function(e) {
  if (e.origin != 'http://example.com') {
    alert('Origin was not http://example.com');
    return;
  }
  ...
}, false);


References

  1. HTML5 Server-Sent Events
  2. Stream Updates with Server-Sent Events - HTML5 Rocks